home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / prgtools / gnustuff / tos / smallt~1 / smallt~1.zoo / mstcomp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-05-26  |  75.3 KB  |  2,918 lines

  1. /***********************************************************************
  2.  *
  3.  *    Byte code compiler.
  4.  *
  5.  ***********************************************************************/
  6.  
  7. /***********************************************************************
  8.  *
  9.  * Copyright (C) 1988, 1989, 1990 Free Software Foundation, Inc.
  10.  * Written by Steve Byrne.
  11.  *
  12.  * This file is part of GNU Smalltalk.
  13.  *
  14.  * GNU Smalltalk is free software; you can redistribute it and/or modify it
  15.  * under the terms of the GNU General Public License as published by the Free
  16.  * Software Foundation; either version 1, or (at your option) any later 
  17.  * version.
  18.  * 
  19.  * GNU Smalltalk is distributed in the hope that it will be useful, but WITHOUT
  20.  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
  21.  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  22.  * more details.
  23.  * 
  24.  * You should have received a copy of the GNU General Public License along with
  25.  * GNU Smalltalk; see the file COPYING.  If not, write to the Free Software
  26.  * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  
  27.  *
  28.  ***********************************************************************/
  29.  
  30.  
  31. /*
  32.  *    Change Log
  33.  * ============================================================================
  34.  * Author      Date       Change 
  35.  * sbyrne    20 May 90      Improved error handling...compiler errors set a flag,
  36.  *              and execution does not occur if the expression to be
  37.  *              executed has compilation errors.
  38.  *
  39.  * sbyrne    16 May 90      Added usage of emacsProcess.
  40.  *
  41.  * sbyrne    20 Apr 90      Fixed compiler to reset the byte code system before
  42.  *              using it.  The problem was if an error occurred, the
  43.  *              old byte code stream was still in use, and further
  44.  *              compilations were losing in a big way.
  45.  *
  46.  * sbyrne    25 Mar 90      Changed cache hit ratio reporting to check for divide
  47.  *              by zero, and to cast the byte counter to double (it
  48.  *              was casting to float and relying on promotion).
  49.  *
  50.  * sbyrne    13 Jan 90      Added support for "thisContext" as a compiler
  51.  *              built-in variable.
  52.  *
  53.  * sbyrne    28 Dec 89      Compiled methods now record their exact number of
  54.  *              byte codes.  Previously, if the byte codes didn't
  55.  *              exactly fill to a word-boundary, there was no way to
  56.  *              distinguish that case.  Now, with the advent of
  57.  *              dumping byte codes from within Smalltalk, this has
  58.  *              become a necessity.
  59.  *
  60.  * sbyrne    27 Dec 89      Realloc literal vec wasn't reallocing in units of
  61.  *              sizeof(OOP), so after a while, the literal vector
  62.  *              wasn't big enough.  Typically most methods don't have
  63.  *              a lot of literals, so this was not a problem.
  64.  *
  65.  * sbyrne     2 Oct 89      Fixed a bug with compilation of cascaded messages.
  66.  *              see HACK ALERT below.
  67.  *
  68.  * sbyrne    21 Sep 89      Made compilation of methods from strings record the
  69.  *              source string.
  70.  *
  71.  * sbyrne    13 Sep 89      Various changes for garbage collector.
  72.  *
  73.  * sbyrne     2 Sep 89      Began adding support for the method descriptor
  74.  *              instance variable.
  75.  *
  76.  * sbyrne     2 Jan 89      I guess it should be stated somewhere: you'll notice
  77.  *              in the code that there are several places where I
  78.  *              could have taken a more "functional" (i.e. LISP
  79.  *              oriented call a function within a function call)
  80.  *              approach.  I chose not to because it can make
  81.  *              debugging easier, it doesn't slow down the code much,
  82.  *              and may help the reader to understand better what's
  83.  *              going on in the code.
  84.  *
  85.  * sbyrne     1 Jan 89      Created.
  86.  *
  87.  */
  88.  
  89.  
  90. #include "mst.h"
  91. #include "mstsym.h"
  92. #include "mstcomp.h"
  93. #include "msttree.h"
  94. #include "mstbyte.h"
  95. #include "mstdict.h"
  96. #include "mstoop.h"
  97. #include "mstinterp.h"
  98. #include "mstlex.h"
  99. #include <setjmp.h>
  100. #ifdef HAS_ALLOCA_H
  101. #include <alloca.h>
  102. #endif
  103. #include <sys/time.h>
  104. #if defined(USG)
  105. #include <sys/times.h>
  106. #endif
  107.  
  108. #define LITERAL_VEC_CHUNK_SIZE        32
  109.  
  110. extern    long        cacheHits, cacheMisses;
  111.  
  112. typedef enum {
  113.   falseJump,
  114.   trueJump,
  115.   unconditionalJump
  116. } JumpType;
  117.  
  118. typedef enum {
  119.   methodContext,
  120.   blockContext
  121. } ContextType;
  122.  
  123. typedef struct CompiledMethodStruct *CompiledMethod;
  124.  
  125. typedef struct MethodInfoStruct {
  126.   OBJ_HEADER;
  127.   OOP        sourceCode;
  128.   OOP        category;
  129. } *MethodInfo;
  130.  
  131. typedef struct FileSegmentStruct {
  132.   OBJ_HEADER;
  133.   OOP        fileName;
  134.   OOP        startPos;
  135.   OOP        length;
  136. } *FileSegment;
  137.  
  138. /* These hold the compiler's notions of the current class for compilations,
  139.  * and the current category that compiled methods are to be placed into */
  140. OOP            thisClass, thisCategory;
  141.  
  142. /* These flags control whether byte codes are printed after compilation,
  143.  * and whether regression testing is in effect (which causes any messages
  144.  * that the system prints out to become constant messages, i.e. no timing
  145.  * information is printed) */
  146. Boolean            declareTracing, regressionTesting;
  147.  
  148. /* If true, the normal execution information is supressed, and the prompt
  149.  * is emitted with a special marker character ahead of it to let the process
  150.  * filter know that the execution has completed. */
  151. Boolean                   emacsProcess = false;
  152.  
  153. static Boolean        hasExtendedSuper, isSuper(), equalConstant(),
  154.               compileWhileLoop(), compileIfStatement(),
  155.             compileIfTrueFalseStatement(), compileAndOrStatement();
  156. static OOP        computeSelector(), makeConstantOOP(),
  157.               getMethodLiterals(), makeNewMethod(), methodNew(),
  158.             methodInfoNew(), fileSegmentNew();
  159. static ByteCodes    optimizeByteCodes(), compileSubExpression(),
  160.               compileSubExpressionWithGoto(),
  161.               compileDefaultValue();
  162. static int        computeLocationIndex(), isSpecialVariable(),
  163.             addConstant(), addSelector(), whichBuiltinSelector(),
  164.             addForcedSelector(), listLength(), addLiteral();
  165. static CompiledMethod    simpleMethodNew();
  166. static void         compileStatement(), compileExpression(),
  167.               compileSimpleExpression(), compileVariable(),
  168.               compileConstant(), compileBlock(),
  169.               compileStatements(), compileUnaryExpr(),
  170.               compileBinaryExpr(), compileKeywordExpr(),
  171.               compileSend(), compileCascadedMessage(),
  172.               compileAssignments(), addMethodClassVariable(),
  173.               compileJump(), compileKeywordList(),
  174.               initLiteralVec(), reallocLiteralVec(),
  175.               compileBlockArguments(), installMethod();
  176.  
  177. /* Used to abort really losing compiles, jumps back to the top level of the
  178.  * compiler */
  179. static jmp_buf        badMethod;
  180.  
  181. /* The vector of literals that the compiler uses to accumulate literal
  182.  * constants into */
  183. static OOP        *literalVec;
  184.  
  185. /* These indicate the current number of literals in the method being compiled
  186.  * and the current maximum allocated size of the literal vector */
  187. static int        numLiterals, literalVecMax;
  188.  
  189. /* HACK ALERT!! HACK ALERT!!  This variable is used for cascading.  The
  190.  * tree structure is all wrong for the code in cascade processing to find
  191.  * the receiver of the initial message.  What this does is when it's true,
  192.  * compileUnaryExpr, compileBinaryExpr, and compileKeywordExpr record
  193.  * its value, and clear the global (to prevent propagation to compilation
  194.  * of subnodes).  After compiling their receiver, if the saved value of
  195.  * the flag is true, they emit a dupStackTop, and continue compilation.
  196.  * Since cascaded sends are relatively rare, I figured that this was a better
  197.  * alternative than passing useless parameters around all the time.
  198.  */
  199. static Boolean        dupMessageReceiver = false;
  200.  
  201. /*
  202.  *    void installInitialMethods()
  203.  *
  204.  * Description
  205.  *
  206.  *    This routine does a very interesting thing.  It installs the inital
  207.  *    method, which is the primitive for "methodsFor:".  It does this by
  208.  *    creating a string that contains the method definition and then passing
  209.  *    this to the parser as an expression to be parsed and compiled.  Once
  210.  *    this has been installed, we can go ahead and begin loading the rest of
  211.  *    the Smalltalk method definitions, but until the "methodsFor:" method is
  212.  *    defined, we cannot begin to deal with
  213.  *    "!Object methodsFor: 'primitives'!".
  214.  *
  215.  */
  216. void installInitialMethods()
  217. {
  218.   char        *methodsForString;
  219.  
  220.   initDefaultCompilationEnvironment();
  221.  
  222.   methodsForString = "\
  223. methodsFor: aCategoryString \
  224.     <primitive: 150> \
  225. ";
  226.   initLexer(true);        /* tell the lexer we're doing internal
  227.                    compiles */
  228.  
  229.   pushSmalltalkString(stringNew(methodsForString));
  230.   yyparse();
  231.   popStream(false);        /* can't close a string! */
  232. }
  233.  
  234. /*
  235.  *    void initDefaultCompilationEnvironment()
  236.  *
  237.  * Description
  238.  *
  239.  *    Does what it says.
  240.  *
  241.  */
  242. void initDefaultCompilationEnvironment()
  243. {
  244.   setCompilationClass(behaviorClass);
  245.   setCompilationCategory(nilOOP);
  246. }
  247.  
  248. /*
  249.  *    void invokeInitBlocks()
  250.  *
  251.  * Description
  252.  *
  253.  *    This function will send a message to Smalltalk (the system dictionary)
  254.  *    asking it to invoke a set of initialization blocks.  There are methods
  255.  *    in Smalltalk that allow for the recording of blocks to be invoked after
  256.  *    image load, and this function sets that process in motion.
  257.  *
  258.  */
  259. void invokeInitBlocks()
  260. {
  261.   /* +++ this will eventually be replaced with the user-level callin */
  262.   prepareExecutionEnvironment();
  263.   pushOOP(smalltalkDictionary);
  264.   sendMessage(internString("doInits"), 0, false);
  265.   interpret();
  266.   finishExecutionEnvironment();
  267. }
  268.  
  269. /*
  270.  *    void setCompilationClass(classOOP)
  271.  *
  272.  * Description
  273.  *
  274.  *    Sets the compiler's notion of the class to compile methods into.
  275.  *
  276.  * Inputs
  277.  *
  278.  *    classOOP: 
  279.  *        An OOP for a Class object to compile method definitions into.
  280.  *
  281.  */
  282. void setCompilationClass(classOOP)
  283. OOP    classOOP;
  284. {
  285.   maybeMoveOOP(classOOP);
  286.   thisClass = classOOP;
  287. }
  288.  
  289. /*
  290.  *    void setCompilationCategory(categoryOOP)
  291.  *
  292.  * Description
  293.  *
  294.  *    Sets the compiler's notion of the current method category
  295.  *
  296.  * Inputs
  297.  *
  298.  *    categoryOOP: 
  299.  *        An OOP that indicates the category to be used.  Typically a
  300.  *        string.
  301.  *
  302.  */
  303. void setCompilationCategory(categoryOOP)
  304. OOP    categoryOOP;
  305. {
  306.   maybeMoveOOP(categoryOOP);
  307.   thisCategory = categoryOOP;
  308. }
  309.  
  310. /*
  311.  *    void copyCompileContext()
  312.  *
  313.  * Description
  314.  *
  315.  *    Called only during a GC flip, this routine copies the current
  316.  *    compilation context variables to new space, since they're part of the
  317.  *    "root set".
  318.  *
  319.  */
  320. void copyCompileContext()
  321. {
  322.   if (!isNil(thisClass)) {
  323.     maybeMoveOOP(thisClass);
  324.   }
  325.  
  326.   if (!isNil(thisCategory)) {
  327.     maybeMoveOOP(thisCategory);
  328.   }
  329. }
  330.  
  331. /*
  332.  *    void executeStatements(temporaries, statements, quiet)
  333.  *
  334.  * Description
  335.  *
  336.  *    Called to compile and execute an "immediate expression"; i.e. a set of
  337.  *    Smalltalk statements that are not part of a method definition.
  338.  *
  339.  * Inputs
  340.  *
  341.  *    temporaries: 
  342.  *        Syntax tree node that represents the temporary variables
  343.  *        associated with the expression.
  344.  *    statements: 
  345.  *        The statements of the expression.  A syntax tree node.
  346.  *    quiet : Flag to indicate either messages are to be output indicating
  347.  *        the commencement of execution and some timing results at the
  348.  *        end of execution.
  349.  *
  350.  */
  351. void executeStatements(temporaries, statements, quiet)
  352. TreeNode temporaries, statements;
  353. Boolean    quiet;
  354. {
  355.   TreeNode    messagePattern;
  356. #if !defined(USG)
  357.   struct timeval startTime, endTime, deltaTime;
  358. #else
  359.   struct timeval startTime, endTime, deltaTime;
  360.   time_t startTime, endTime, deltaTime;
  361.   struct tms dummy;
  362. #endif
  363.   OOP        returnedValue;
  364.  
  365.   setCompilationClass(objectClass);
  366.  
  367.   messagePattern = makeUnaryExpr(nil, "executeStatements");
  368.   compileMethod(makeMethod(messagePattern, temporaries, 0, statements), false);
  369.   if (hadError) {        /* don't execute on error */
  370.     return;
  371.   }
  372.  
  373.   /* send a message to NIL, which will find this synthetic method definition
  374.      in Object and execute it */
  375.   prepareExecutionEnvironment();
  376.   pushOOP(nilOOP);
  377.   if (!quiet) {
  378.     printf("\nExecution begins...\n");
  379.   }
  380.   sendMessage(internString("executeStatements"), 0, false);
  381.   byteCodeCounter = 0;
  382. #if !defined(USG)
  383.   gettimeofday(&startTime, nil);
  384.   interpret();
  385.   gettimeofday(&endTime, nil);
  386. #else
  387.   startTime = times(&dummy);
  388.   interpret();
  389.   endTime = times(&dummy);
  390. #endif
  391.   returnedValue = finishExecutionEnvironment();
  392.   if (!quiet) {
  393.     if (!regressionTesting) {
  394.       printf("%d byte codes executed\n", byteCodeCounter);
  395. #if !defined(USG)
  396.       deltaTime.tv_sec = endTime.tv_sec - startTime.tv_sec;
  397.       deltaTime.tv_usec = endTime.tv_usec - startTime.tv_usec;
  398.       if (deltaTime.tv_usec < 0) {
  399.     deltaTime.tv_sec--;
  400.     deltaTime.tv_usec += 1000000;
  401.       }
  402.       if (deltaTime.tv_sec == 0 && deltaTime.tv_usec == 0) {
  403.     deltaTime.tv_usec = 1;    /* fake a non-zero amount of time */
  404.       }
  405.       printf("which took %d.%d seconds, giving %f bytecodes/sec\n",
  406.          deltaTime.tv_sec, deltaTime.tv_usec, (double)byteCodeCounter/
  407.          (deltaTime.tv_sec + deltaTime.tv_usec/1000000.0));
  408. #else
  409.       deltaTime = endTime - startTime;
  410.       if (deltaTime <= 0){
  411.      deltaTime = 1;        /* it could be zero which would core dump */
  412.       }
  413.       printf("which took %d.%d seconds, giving %f bytecodes/sec\n",
  414.          deltaTime/gethz(), deltaTime%gethz(), (float)byteCodeCounter/
  415.          (deltaTime / (double) gethz()));
  416. #endif
  417.       if (cacheHits + cacheMisses) {
  418.     printf("%d cache hits, %d misses %f hit ratio\n",
  419.            cacheHits, cacheMisses,
  420.            (float)cacheHits / (cacheHits + cacheMisses));
  421.       } else {
  422.     printf("%d cache hits, %d misses\n",
  423.            cacheHits, cacheMisses);
  424.       }
  425.       
  426. /*    approx 25% of byte codes are sends
  427.       printf("sends / bytecodes = %.1f\n",
  428.          (cacheHits + cacheMisses) * 100.0 / byteCodeCounter );
  429. */
  430. #ifdef countingByteCodes 
  431.       printByteCodeCounts();
  432. #endif
  433. #ifdef collision_checking
  434.       { int i;
  435.     extern int collide[];
  436.     for (i = 0; i < 2048; i++) {
  437.       if (collide[i]) {
  438.         printf("collide[%d] = %d\n", i, collide[i]);
  439.       }
  440.     }
  441.       }
  442. #endif /* collision_checking */
  443.     }
  444.  
  445.     printf("returned value is ");
  446.     printObject(returnedValue);
  447.     printf("\n");
  448.   }
  449. }
  450.  
  451.  
  452. /*
  453.  *    void compileMethod(method)
  454.  *
  455.  * Description
  456.  *
  457.  *    Compile the code for a complete method definition.  Special cases for
  458.  *    methods that don't return a value explicitly by returning "self".
  459.  *    Actually creates the CompiledMethod object and installs it in the
  460.  *    current method dictionary with the selector derived from the method
  461.  *    expression.
  462.  *
  463.  * Inputs
  464.  *
  465.  *    method: A syntax tree node for a method definition.
  466.  *
  467.  */
  468. void compileMethod(method)
  469. TreeNode method;
  470. {
  471.   TreeNode    statement;
  472.   OOP        selector;
  473.   ByteCodes    byteCodes;
  474.  
  475.   dupMessageReceiver = false;
  476.  
  477.   initCompiler();
  478.   declareArguments(method->vMethod.selectorExpr);
  479.   declareTemporaries(method->vMethod.temporaries);
  480.  
  481.   if (setjmp(badMethod) == 0) {
  482.     for (statement = method->vMethod.statements; statement;
  483.      statement = statement->vExpr.expression) {
  484.       compileStatement(statement->vExpr.receiver);
  485.       if (statement->vExpr.receiver->nodeType != returnExprType) {
  486.     if (statement->vExpr.expression == nil) {
  487.       /* compile a return of self */
  488.       compileByte(returnIndexed | receiverIndex);
  489.     } else {
  490.       /* ignore the result of the last statement if it's not used */
  491.       compileByte(popStackTop);
  492.     }
  493.       }
  494.     }
  495.  
  496.     if (method->vMethod.statements == nil) {
  497.       /* special case an empty statement body to return self */
  498.       /* ??? this could compile some kind of primitive failure message, 
  499.      I guess */
  500.       compileByte(returnIndexed | receiverIndex);
  501.     }
  502.  
  503.     if (hasExtendedSuper) {
  504.       addMethodClassVariable();
  505.     }
  506.  
  507.     selector = computeSelector(method->vMethod.selectorExpr);
  508.     byteCodes = getByteCodes();
  509.     byteCodes = optimizeByteCodes(byteCodes);
  510.  
  511.     installMethod(selector, method->vMethod.primitiveIndex,
  512.           getArgCount(), getTempCount(),
  513.           getMethodLiterals(), byteCodes);
  514.   } else {
  515.     hadError = true;
  516.   }
  517.  
  518.   undeclareTemporaries(method->vMethod.temporaries);
  519.   undeclareArguments(method->vMethod.selectorExpr);
  520.   freeTree(method);
  521. }
  522.  
  523. /*
  524.  *    static void compileStatement(stmt)
  525.  *
  526.  * Description
  527.  *
  528.  *    Compiles a statement expression, including return expressions.
  529.  *
  530.  * Inputs
  531.  *
  532.  *    stmt  : A stmt tree node.
  533.  *
  534.  */
  535. static void compileStatement(stmt)
  536. TreeNode stmt;
  537. {
  538.   int        index;
  539.  
  540.   switch (stmt->nodeType) {
  541.   case constExprType:
  542.   case blockNodeType:
  543.     index = -1;
  544.     break;
  545.  
  546.   default:
  547.     index = isSpecialVariable(stmt->vExpr.receiver);
  548.   }
  549.  
  550.   if (index < 0) {
  551.     if (stmt->nodeType == returnExprType) {
  552.       compileExpression(stmt->vExpr.receiver);
  553.       compileByte(returnMethodStackTop);
  554.     } else {
  555.       compileExpression(stmt);
  556.     }
  557.   } else {
  558.     if (stmt->nodeType == returnExprType) {
  559.       /* return one of {self, true, false, nil} */
  560.       compileByte(returnIndexed | index);
  561.     } else {
  562.       compileExpression(stmt);
  563.     }
  564.   }
  565. }
  566.  
  567. /*
  568.  *    static void compileExpression(expr)
  569.  *
  570.  * Description
  571.  *
  572.  *    Compile an arbitrary expression, including an assignment expression.
  573.  *
  574.  * Inputs
  575.  *
  576.  *    expr  : A syntax tree node for an expression, including assignments.
  577.  *
  578.  */
  579. static void compileExpression(expr)
  580. TreeNode expr;
  581. {
  582.   if (expr->nodeType == assignExprType) {
  583.     compileSimpleExpression(expr->vExpr.expression);
  584.     compileAssignments(expr->vExpr.receiver);
  585.   } else {
  586.     compileSimpleExpression(expr);
  587.   }
  588. }
  589.  
  590. /*
  591.  *    static void compileSimpleExpression(expr)
  592.  *
  593.  * Description
  594.  *
  595.  *    The basic expression compiler.  Can be called recursively.  Dispatches
  596.  *    based on the type of the expression to different routines that
  597.  *    specialize in compilations for that expression.
  598.  *
  599.  * Inputs
  600.  *
  601.  *    expr  : A syntax tree node for some kind of expression.
  602.  *
  603.  */
  604. static void compileSimpleExpression(expr)
  605. TreeNode expr;
  606. {
  607.   switch (expr->nodeType) {
  608.   case variableNodeType:
  609.     compileVariable(expr);
  610.     break;
  611.   case constExprType:
  612.     compileConstant(expr);
  613.     break;
  614.   case blockNodeType:
  615.     compileBlock(expr);
  616.     break;
  617.   case unaryExprType:
  618.     compileUnaryExpr(expr);
  619.     break;
  620.   case binaryExprType:
  621.     compileBinaryExpr(expr);
  622.     break;
  623.   case keywordExprType:
  624.     compileKeywordExpr(expr);
  625.     break;
  626.   case cascadedMessageNodeType:
  627.     compileCascadedMessage(expr);
  628.     break;
  629.   default:
  630.     compileExpression(expr);
  631.   }
  632. }
  633.  
  634. /*
  635.  *    static void compileVariable(varName)
  636.  *
  637.  * Description
  638.  *
  639.  *    Compile code to push the value of a variable onto the stack.  The
  640.  *    special variables, self, true, false, super, and thisContext, are
  641.  *    handled specially.  For other variables, different code is emitted
  642.  *    depending on where the variable lives, such as in a global variable or
  643.  *    in a method temporary.
  644.  *
  645.  * Inputs
  646.  *
  647.  *    varName: 
  648.  *        A syntax tree node that indicates a variable name.
  649.  *
  650.  */
  651. static void compileVariable(varName)
  652. TreeNode varName;
  653. {
  654.   SymbolEntry    variable;
  655.   int        index, location;
  656.  
  657.   index = isSpecialVariable(varName);
  658.   if (index >= 0) {
  659.     compileByte(pushSpecial | index);
  660.     return;
  661.   }
  662.  
  663.   if (internString(varName->vList.name) == thisContextSymbol) {
  664.     compileByte(pushActiveContext);
  665.     return;
  666.   }
  667.  
  668.   variable = findVariable(varName->vList.name);
  669.   if (variable == nil) {
  670.     errorf("Undefined variable %s referenced", varName->vList.name);
  671.     longjmp(badMethod, 1);
  672.   }
  673.   
  674.   if (variable->scope == temporaryScope || variable->scope == receiverScope) {
  675.     if (variable->varIndex <= 15) {
  676.       if (variable->scope == temporaryScope) {
  677.     compileByte(pushTemporaryVariable | variable->varIndex);
  678.       } else {
  679.     compileByte(pushReceiverVariable | variable->varIndex);
  680.       }
  681.     } else {
  682.       compileByte(pushIndexed);
  683.       location = (variable->scope == temporaryScope)
  684.     ? temporaryLocation : receiverLocation;
  685.       compileByte(location | variable->varIndex);
  686.     }
  687.   } else {
  688.     if (variable->varIndex <= 31) {
  689.       compileByte(pushLitVariable | variable->varIndex);
  690.     } else {
  691.       /* ??? check for variable index too large here? */
  692.       compileByte(pushIndexed);
  693.       compileByte(litVarLocation | variable->varIndex);
  694.     }
  695.   }
  696.   freeSymbolEntry(variable);
  697. }
  698.  
  699. /*
  700.  *    static void compileConstant(constExpr)
  701.  *
  702.  * Description
  703.  *
  704.  *    Compile an expression that pushes a constant expression onto the stack.
  705.  *    Special cases out the constants that the byte code interpreter knows
  706.  *    about, which are the integers in the range -1 to 2.  Tries to emit the
  707.  *    shortest possible byte sequence.
  708.  *
  709.  * Inputs
  710.  *
  711.  *    constExpr: 
  712.  *        A syntax tree node that represents a literal constant.
  713.  *
  714.  */
  715. static void compileConstant(constExpr)
  716. TreeNode constExpr;
  717. {
  718.   int        index;
  719.  
  720.   index = addConstant(constExpr);
  721.   if (index < 0) {
  722.     compileByte(pushSpecial | (index + 3 + 5));
  723.   } else if (index <= 31) {
  724.     compileByte(pushLitConstant | index);
  725.   } else {
  726.     compileByte(pushIndexed);
  727.     compileByte(litConstLocation | index);
  728.   }
  729. }
  730.  
  731. /*
  732.  *    static void compileBlock(blockExpr)
  733.  *
  734.  * Description
  735.  *
  736.  *    Compile the expressions for a block.  Also, emits code to create the
  737.  *    block, and then to skip around it.  The block will have its initial
  738.  *    byte pointer pointing to two bytes past the long jump instruction, so
  739.  *    that when the block is invoked it will start off at the first byte of
  740.  *    the block.
  741.  *    
  742.  *
  743.  * Inputs
  744.  *
  745.  *    blockExpr: 
  746.  *        A syntax tree node for a block expression.
  747.  *
  748.  */
  749. static void compileBlock(blockExpr)
  750. TreeNode blockExpr;
  751. {
  752.   ByteCodes    currentByteCodes, blockByteCodes;
  753.  
  754.   currentByteCodes = saveByteCodeArray(); /* ??? don't like this name */
  755.  
  756.   declareBlockArguments(blockExpr->vMethod.temporaries);
  757.   compileBlockArguments(blockExpr->vMethod.temporaries);
  758.   compileStatements(blockExpr->vMethod.statements, true);
  759.   undeclareBlockArguments(blockExpr->vMethod.temporaries);
  760.  
  761.   blockByteCodes = getByteCodes();
  762.   restoreByteCodeArray(currentByteCodes);
  763.  
  764.   /* emit standard byte sequence to invoke a block:
  765.    *   push current context
  766.    *   push number of block args
  767.    *   blockCopy: send
  768.    *   long jump around block bytecodes
  769.    *   <block byte codes>...
  770.    */
  771.   compileByte(pushActiveContext);
  772.   compilePushIntConstant(listLength(blockExpr->vMethod.temporaries));
  773.   compileByte(blockCopyColonSpecial);
  774.   compileByte(jumpLong | (byteCodeLength(blockByteCodes)/256)+4);
  775.   compileByte(byteCodeLength(blockByteCodes) & 255);
  776.   compileAndFreeByteCodes(blockByteCodes);
  777.  
  778. }
  779.  
  780. /*
  781.  *    static void compileBlockArguments(args)
  782.  *
  783.  * Description
  784.  *
  785.  *    On entry to a block, its arguments (if any) are on the stack.  Since
  786.  *    there is no way to refer to them via byte codes on the stack, the
  787.  *    correct procedure is to pop them into temporary locations in the method
  788.  *    itself.  Since we're popping from a stack, we need to pop the arguments
  789.  *    in reverse order from the order that they are declared.  Args is a list
  790.  *    of TreeNodes in declaration order, so we recurse to get the last one,
  791.  *    pop it, get the penultimate, pop it, etc.  This routine works even if
  792.  *    args is nil, in which case the block had no arguments.
  793.  *
  794.  * Inputs
  795.  *
  796.  *    args  : a TreeNode of type ListNode that is a list of argument names to
  797.  *        the block being compiled.
  798.  *
  799.  */
  800. static void compileBlockArguments(args)
  801. TreeNode args;
  802. {
  803.   SymbolEntry    variable;
  804.  
  805.   if (args == nil) {
  806.     return;
  807.   }
  808.  
  809.   compileBlockArguments(args->vList.next);
  810.   variable = findVariable(args->vList.name);
  811.   if (variable->varIndex <= 7) {
  812.     compileByte(popTemporaryVariable | variable->varIndex);
  813.   } else {
  814.     compileByte(popStoreIndexed);
  815.     compileByte(temporaryLocation | variable->varIndex);
  816.   }
  817.   freeSymbolEntry(variable);
  818. }
  819.  
  820. /*
  821.  *    static void compileStatements(statementList, isBlock)
  822.  *
  823.  * Description
  824.  *
  825.  *    Compiles all of the statements in statement list.  Makes the final
  826.  *    instruction of the block be a return top of stack, if the final
  827.  *    statement isn't a return (^).
  828.  *
  829.  * Inputs
  830.  *
  831.  *    statementList: 
  832.  *        A TreeNode of type ExprNode that is the list of statements in
  833.  *        the block.  If it is nil, the block's return value is nil.
  834.  *    isBlock:A boolean.  If true, these statements are from a block
  835.  *        context.  If false, they are just an ordinary statement list.
  836.  */
  837. static void compileStatements(statementList, isBlock)
  838. TreeNode statementList;
  839. Boolean    isBlock;
  840. {
  841.   TreeNode    stmt;
  842.  
  843.   if (statementList == nil) {
  844.     if (isBlock) {
  845.       compileByte(returnIndexed | nilIndex);
  846.     } else {
  847.       compileByte(pushSpecial | nilIndex);
  848.     }
  849.     return;
  850.   }
  851.   
  852.   for(stmt = statementList ; stmt; stmt = stmt->vExpr.expression) {
  853.     compileStatement(stmt->vExpr.receiver);
  854.     if (stmt->vExpr.expression == nil) {
  855.       if (stmt->vExpr.receiver->nodeType != returnExprType) {
  856.     /* if last statement isn't a return, then return the value on the
  857.        stack as the result.  For non-block contexts, returning the top
  858.        of the stack is the default, so it's ok.*/
  859.     if (isBlock) {
  860.       compileByte(returnBlockStackTop);
  861.     }
  862.       }
  863.     } else {
  864.       /* throw away the value on the top of the stack...we don't need it
  865.      for all but the last one. */
  866.       compileByte(popStackTop);
  867.     }
  868.   }
  869. }
  870.  
  871.  
  872. /*
  873.  *    static void compileUnaryExpr(expr)
  874.  *
  875.  * Description
  876.  *
  877.  *    Compile code to evaluate a unary expression.  Special cases sends to
  878.  *    "super" and duplicates the receiver's value if this is part of a
  879.  *    cascaded message send.
  880.  *
  881.  * Inputs
  882.  *
  883.  *    expr  : A syntax tree node for a unary expression.
  884.  *
  885.  */
  886. static void compileUnaryExpr(expr)
  887. TreeNode expr;
  888. {
  889.   OOP        selector;
  890.   int        selectorIndex;
  891.   Boolean    savedDupFlag;
  892.  
  893.   savedDupFlag = dupMessageReceiver;
  894.   dupMessageReceiver = false;
  895.  
  896.   selector = expr->vExpr.selector;
  897.   selectorIndex = addSelector(selector);
  898.  
  899.   if (expr->vExpr.receiver != nil) {
  900.     compileExpression(expr->vExpr.receiver);
  901.     if (savedDupFlag) {
  902.       compileByte(dupStackTop);
  903.     }
  904.     if (isSuper(expr->vExpr.receiver)) {
  905.       if (selectorIndex < 0) {
  906.     selectorIndex = addForcedSelector(selector);
  907.       }
  908.       hasExtendedSuper = true;
  909.       if (selectorIndex <= 31) {
  910.     compileByte(sendSuper1ExtByte);
  911.     compileByte(selectorIndex);
  912.       } else {
  913.     compileByte(sendSuper2ExtByte);
  914.     compileByte(0);
  915.     compileByte(selectorIndex);
  916.       }
  917.       return;
  918.     }
  919.   }
  920.   
  921.   if (isNil(selector)) {
  922.     errorf("Nil selector in unary expression");
  923.     longjmp(badMethod, 1);
  924.   }
  925.  
  926.   if (selectorIndex < 0) {
  927.     compileByte(-selectorIndex);
  928.   } else {
  929.     compileSend(selectorIndex, 0);
  930.   }
  931. }
  932.  
  933. /*
  934.  *    static void compileBinaryExpr(expr)
  935.  *
  936.  * Description
  937.  *
  938.  *    Compiles code for a binary message.  Special cases sends to super, as
  939.  *    they get different byte codes.  Also, checks to see if it's the first
  940.  *    part of a cascaded message send and if so emits code to duplicate the
  941.  *    stack top after the evaluation of the receiver for use by the
  942.  *    subsequent cascaded expressions.
  943.  *
  944.  * Inputs
  945.  *
  946.  *    expr  : A syntax tree node for a binary expression.
  947.  *
  948.  */
  949. static void compileBinaryExpr(expr)
  950. TreeNode expr;
  951. {
  952.   OOP        selector;
  953.   int        selectorIndex;
  954.   Boolean    savedDupFlag;
  955.  
  956.   savedDupFlag = dupMessageReceiver;
  957.   dupMessageReceiver = false;
  958.  
  959.   selector = expr->vExpr.selector;
  960.   selectorIndex = addSelector(selector);
  961.  
  962.   if (expr->vExpr.receiver) {
  963.     compileExpression(expr->vExpr.receiver);
  964.     if (savedDupFlag) {
  965.       compileByte(dupStackTop);
  966.     }
  967.   }
  968.   if (expr->vExpr.expression) {
  969.     compileExpression(expr->vExpr.expression);
  970.   }
  971.  
  972.   if (expr->vExpr.receiver) {
  973.     if (isSuper(expr->vExpr.receiver)) {
  974.       if (selectorIndex < 0) {
  975.     selectorIndex = addForcedSelector(selector);
  976.       }
  977.       hasExtendedSuper = true;
  978.       if (selectorIndex <= 31) {
  979.     compileByte(sendSuper1ExtByte);
  980.     compileByte((1 << 5) | selectorIndex);
  981.       } else {
  982.     compileByte(sendSuper2ExtByte);
  983.     compileByte(1);
  984.     compileByte(selectorIndex);
  985.       }
  986.     }
  987.   }
  988.  
  989.   if (isNil(selector)) {
  990.     errorf("nil selector in binary expression");
  991.     longjmp(badMethod, 1);
  992.   }
  993.  
  994.   if (selectorIndex < 0) {
  995.     compileByte(-selectorIndex);
  996.   } else {
  997.     compileSend(selectorIndex, 1);
  998.   }
  999. }
  1000.  
  1001. /*
  1002.  *    static void compileKeywordExpr(expr)
  1003.  *
  1004.  * Description
  1005.  *
  1006.  *    Compile an keyword message send.  Special cases out while loops, the 4
  1007.  *    kinds of if tests, and the conditional "and" and conditional "or"
  1008.  *    messages.  If the expression isn't one of these, the expression is
  1009.  *    evaluated normally.  Has special hacks to support the duplication of
  1010.  *    the receiver's value in the case of the first part of a cascaded
  1011.  *    message send.
  1012.  *
  1013.  * Inputs
  1014.  *
  1015.  *    expr  : A syntax tree node that represents a keyword message send
  1016.  *        expression.
  1017.  *
  1018.  */
  1019. static void compileKeywordExpr(expr)
  1020. TreeNode expr;
  1021. {
  1022.   OOP        selector;
  1023.   int        selectorIndex, numArgs;
  1024.   Boolean    savedDupFlag;
  1025.  
  1026.   savedDupFlag = dupMessageReceiver;
  1027.   dupMessageReceiver = false;
  1028.  
  1029.   selector = computeSelector(expr);
  1030.  
  1031.   /* check for optimized cases of messages to booleans and handle them
  1032.      specially */
  1033.   if (selector == whileTrueColonSymbol || selector == whileFalseColonSymbol) {
  1034.     if (compileWhileLoop(selector, expr)) {
  1035.       return;
  1036.     }
  1037.   }
  1038.  
  1039.   if (expr->vExpr.receiver) {
  1040.     compileExpression(expr->vExpr.receiver);
  1041.     if (savedDupFlag) {
  1042.       compileByte(dupStackTop);
  1043.     }
  1044.   }
  1045.  
  1046.   if (selector == ifTrueColonSymbol || selector == ifFalseColonSymbol) {
  1047.     if (compileIfStatement(selector, expr->vExpr.expression)) {
  1048.       return;
  1049.     }
  1050.   } else if (selector == ifTrueColonIfFalseColonSymbol
  1051.          || selector == ifFalseColonIfTrueColonSymbol) {
  1052.     if (compileIfTrueFalseStatement(selector, expr->vExpr.expression)) {
  1053.       return;
  1054.     }
  1055.   } else if (selector == andColonSymbol || selector == orColonSymbol) {
  1056.     if (compileAndOrStatement(selector, expr->vExpr.expression)) {
  1057.       return;
  1058.     }
  1059.   }
  1060.  
  1061.   selectorIndex = addSelector(selector);
  1062.   numArgs = listLength(expr->vExpr.expression);
  1063.  
  1064.   compileKeywordList(expr->vExpr.expression);
  1065.  
  1066.   if (expr->vExpr.receiver) {
  1067.     if (isSuper(expr->vExpr.receiver)) {
  1068.       if (selectorIndex < 0) {
  1069.     selectorIndex = addForcedSelector(selector);
  1070.       }
  1071.       hasExtendedSuper = true;
  1072.       if (selectorIndex <= 31 && numArgs <= 7) {
  1073.     compileByte(sendSuper1ExtByte);
  1074.     compileByte((numArgs << 5) | selectorIndex);
  1075.       } else {
  1076.     compileByte(sendSuper2ExtByte);
  1077.     compileByte(numArgs);
  1078.     compileByte(selectorIndex);
  1079.       }
  1080.       return;
  1081.     }
  1082.   }
  1083.  
  1084.   if (selectorIndex < 0) {
  1085.     compileByte(-selectorIndex);
  1086.   } else {
  1087.     compileSend(selectorIndex, numArgs);
  1088.   }
  1089. }
  1090.  
  1091. /*
  1092.  *    static void compileKeywordList(list)
  1093.  *
  1094.  * Description
  1095.  *
  1096.  *    Emit code to evaluate each argument to a keyword message send.
  1097.  *
  1098.  * Inputs
  1099.  *
  1100.  *    list  : A list of expressions that represents the arguments to be
  1101.  *        evaluated.  A syntax tree node.
  1102.  *
  1103.  */
  1104. static void compileKeywordList(list)
  1105. TreeNode list;
  1106. {
  1107.   for (; list; list = list->vList.next) {
  1108.     compileExpression(list->vList.value);
  1109.   }
  1110. }
  1111.  
  1112. /*
  1113.  *    static Boolean compileWhileLoop(selector, expr)
  1114.  *
  1115.  * Description
  1116.  *
  1117.  *    Special case compilation of a #whileTrue: or #whileFalse: loop.
  1118.  *
  1119.  * Inputs
  1120.  *
  1121.  *    selector: 
  1122.  *        Symbol, one of #whileTrue: or #whileFalse:.
  1123.  *    expr  : An expression that represents the entire while loop.
  1124.  *
  1125.  * Outputs
  1126.  *
  1127.  *    True if byte codes were emitted, false if not.  If either the receiver
  1128.  *    and the argument to the while message are not block expressions, this
  1129.  *    routine cannot do it's job, and so returns false to indicate as much.
  1130.  */
  1131. static Boolean compileWhileLoop(selector, expr)
  1132. OOP    selector;
  1133. TreeNode expr;
  1134. {
  1135.   int        whileLoopLen, startLoopLen;
  1136.   ByteCodes    receiverExprCodes, whileExprCodes;
  1137.  
  1138.   if (expr->vExpr.receiver->nodeType != blockNodeType
  1139.       || expr->vExpr.expression->vList.value->nodeType != blockNodeType) {
  1140.     return (false);
  1141.   }
  1142.  
  1143.   startLoopLen = currentByteCodeLength();
  1144.  
  1145.   receiverExprCodes = compileSubExpression(expr->vExpr.receiver);
  1146.   whileExprCodes = compileSubExpression(expr->vExpr.expression->vList.value);
  1147.   compileAndFreeByteCodes(receiverExprCodes);
  1148.  
  1149.   /* skip around the while expr and the pop stack top and the 2 byte goto
  1150.      that follows if the condition doesn't match the while test. */
  1151.   compileJump(byteCodeLength(whileExprCodes)+3,
  1152.           (selector == whileTrueColonSymbol) ? falseJump : trueJump);
  1153.  
  1154.   compileAndFreeByteCodes(whileExprCodes);
  1155.   compileByte(popStackTop);    /* we don't care about while expr's value */
  1156.  
  1157.   /* +2 since we're using a 2 byte jump instruction here, so we have to
  1158.      skip back over it in addition to the other instructions */
  1159.   whileLoopLen = currentByteCodeLength() - startLoopLen +2;
  1160.   /* this is a backwards branch, but you can't tell it */
  1161.   compileByte(jumpLong | (4 - ((whileLoopLen+255)/256)));
  1162.   compileByte((-whileLoopLen) & 255);
  1163.   
  1164.   compileByte(pushSpecial | nilIndex);
  1165.   return (true);
  1166. }
  1167.  
  1168. /*
  1169.  *    static Boolean compileIfTrueFalseStatement(selector, expr)
  1170.  *
  1171.  * Description
  1172.  *
  1173.  *    Special case compile of the code for #ifTrue:false: and #ifFalse:true:
  1174.  *    messages.
  1175.  *
  1176.  * Inputs
  1177.  *
  1178.  *    selector: 
  1179.  *        Symbol, one of #ifTrue:false: or #ifFalse:true:
  1180.  *    expr  : An tree node that represents the expressions for the first and
  1181.  *        second arguments to the message.  If either is not a block type
  1182.  *        expression, no byte codes are emitted, and this routine
  1183.  *        returns false.
  1184.  *
  1185.  * Outputs
  1186.  *
  1187.  *    True if the byte codes to perform the test were successfully emitted,
  1188.  *    false if not.
  1189.  */
  1190. static Boolean compileIfTrueFalseStatement(selector, expr)
  1191. OOP    selector;
  1192. TreeNode expr;
  1193. {
  1194.   ByteCodes    trueByteCodes, falseByteCodes;
  1195.  
  1196.   if (expr->vList.value->nodeType != blockNodeType
  1197.       || expr->vList.next->vList.value->nodeType != blockNodeType) {
  1198.     return (false);
  1199.   }
  1200.  
  1201.   if (selector == ifTrueColonIfFalseColonSymbol) {
  1202.     falseByteCodes = compileSubExpression(expr->vList.next->vList.value);
  1203.     trueByteCodes =
  1204.       compileSubExpressionWithGoto(expr->vList.value,
  1205.                    byteCodeLength(falseByteCodes));
  1206.   } else {
  1207.     falseByteCodes = compileSubExpression(expr->vList.value);
  1208.     trueByteCodes =
  1209.       compileSubExpressionWithGoto(expr->vList.next->vList.value,
  1210.                    byteCodeLength(falseByteCodes));
  1211.   }
  1212.  
  1213.   compileJump(byteCodeLength(trueByteCodes), falseJump);
  1214.   compileAndFreeByteCodes(trueByteCodes);
  1215.   compileAndFreeByteCodes(falseByteCodes);
  1216.   return (true);
  1217. }
  1218.  
  1219. /*
  1220.  *    static Boolean compileIfStatement(selector, expr)
  1221.  *
  1222.  * Description
  1223.  *
  1224.  *    Special case compile of code for an #ifTrue: or #ifFalse: message.  The
  1225.  *    default value of an "if" type message is nil.
  1226.  *
  1227.  * Inputs
  1228.  *
  1229.  *    selector: 
  1230.  *        Symbol, one of #ifTrue: or #ifFalse:
  1231.  *    expr  : An expression to be evaluated if the given condition holds.
  1232.  *
  1233.  * Outputs
  1234.  *
  1235.  *    True if byte codes were emitted, false if not (as is the case if the
  1236.  *    expression following the selector is not a block).
  1237.  */
  1238. static Boolean compileIfStatement(selector, expr)
  1239. OOP    selector;
  1240. TreeNode expr;
  1241. {
  1242.   ByteCodes    thenByteCodes, defaultByteCodes;
  1243.   Boolean    hasElse;
  1244.  
  1245.   if (expr->vList.value->nodeType != blockNodeType) {
  1246.     return (false);
  1247.   }
  1248.  
  1249.   thenByteCodes = compileSubExpression(expr->vList.value);
  1250.   defaultByteCodes = compileDefaultValue(nilIndex,
  1251.                      byteCodeLength(thenByteCodes));
  1252.   compileJump(byteCodeLength(defaultByteCodes),
  1253.           (selector == ifTrueColonSymbol) ? trueJump : falseJump);
  1254.   compileAndFreeByteCodes(defaultByteCodes);
  1255.   compileAndFreeByteCodes(thenByteCodes);
  1256.   return (true);
  1257. }
  1258.  
  1259.  
  1260.  
  1261. /*
  1262.  *    static Boolean compileAndOrStatement(selector, expr)
  1263.  *
  1264.  * Description
  1265.  *
  1266.  *    Special casing for and: an or: messages.  Emits code that jumps on the
  1267.  *    condition (true for and:, false for or:) to the actual expression for
  1268.  *    the block that's the argument to the message.  Then emits code that
  1269.  *    pushes the default value onto the stack, which will be only executed in
  1270.  *    the failure cases.  Then emits the code for the evaluation of the block
  1271.  *    that's the argument to the message.
  1272.  *
  1273.  * Inputs
  1274.  *
  1275.  *    selector: 
  1276.  *        A Symbol, either #and: or #or:.
  1277.  *    expr  : A syntax tree piece that represents the expressions contained
  1278.  *        in the block that's passed as an argument of the message.
  1279.  *
  1280.  * Outputs
  1281.  *
  1282.  *    Returns true if the code was successfully emitted, and false if the
  1283.  *    code was not (such as a non-block following the selector).
  1284.  */
  1285. static Boolean compileAndOrStatement(selector, expr)
  1286. OOP    selector;
  1287. TreeNode expr;
  1288. {
  1289.   ByteCodes    blockByteCodes, defaultByteCodes;
  1290.   int        blockLen;
  1291.   
  1292.   /* I elected for simplicty sake to just emit the same kind of code always,
  1293.      and not try to save a byte by using the jump false. */
  1294.  
  1295.   if (expr->vList.value->nodeType != blockNodeType) {
  1296.     return (false);
  1297.   }
  1298.  
  1299.   blockByteCodes = compileSubExpression(expr->vList.value);
  1300.   blockLen = byteCodeLength(blockByteCodes);
  1301.   defaultByteCodes = compileDefaultValue((selector == andColonSymbol)
  1302.                      ? falseIndex : trueIndex, blockLen);
  1303.   compileJump(byteCodeLength(defaultByteCodes),
  1304.           (selector == andColonSymbol) ? trueJump : falseJump);
  1305.   compileAndFreeByteCodes(defaultByteCodes);
  1306.   compileAndFreeByteCodes(blockByteCodes);
  1307.   return (true);
  1308. }
  1309.  
  1310. /*
  1311.  *    static ByteCodes compileDefaultValue(litIndex, realExprLen)
  1312.  *
  1313.  * Description
  1314.  *
  1315.  *    Compiles and returns a byte code sequence that represents the default
  1316.  *    value of a conditional expression, such as IfTrue: when the receiver is
  1317.  *    false.  The sequence causes the default value to be pushed on the
  1318.  *    stack, and then the byte codes for the non-default value to be jumped
  1319.  *    around.
  1320.  *
  1321.  * Inputs
  1322.  *
  1323.  *    litIndex: 
  1324.  *        The index for the value to push, typcially one of true, false,
  1325.  *        or nil.  Used as part of a pushSpecial byte code.
  1326.  *    realExprLen: 
  1327.  *        A C int that represents the length of the byte codes that are
  1328.  *        evaluated in the non-default case.
  1329.  *
  1330.  * Outputs
  1331.  *
  1332.  *    A sequence of byte codes pushes the default value and skips around the
  1333.  *    computation of the real value.
  1334.  */
  1335. static ByteCodes compileDefaultValue(litIndex, realExprLen)
  1336. int    litIndex, realExprLen;
  1337. {
  1338.   ByteCodes    currentByteCodes, defaultByteCodes;
  1339.  
  1340.   currentByteCodes = saveByteCodeArray(); /* ??? don't like this name */
  1341.  
  1342.   compileByte(pushSpecial | litIndex);
  1343.   compileJump(realExprLen, unconditionalJump);
  1344.  
  1345.   defaultByteCodes = getByteCodes();
  1346.   restoreByteCodeArray(currentByteCodes);
  1347.  
  1348.   return (defaultByteCodes);
  1349. }
  1350.  
  1351. /*
  1352.  *    static ByteCodes compileSubExpression(expr)
  1353.  *
  1354.  * Description
  1355.  *
  1356.  *    Compile a "block" in a separate context and return the resulting
  1357.  *    bytecodes.  The block will not have argument declarations as it's only
  1358.  *    the code for things like ifTrue:, and:, whileTrue:, etc.  It is
  1359.  *    compiled as a list of statements such that the last statement leaves
  1360.  *    the value that is produced on the stack, as the value of the "block".
  1361.  *
  1362.  * Inputs
  1363.  *
  1364.  *    expr  : A "block" TreeNode that has no arguments. 
  1365.  *
  1366.  * Outputs
  1367.  *
  1368.  *    A ByteCodes vector of byte codes that represent the execution of the
  1369.  *    statements in the "block".
  1370.  */
  1371. static ByteCodes compileSubExpression(expr)
  1372. TreeNode expr;
  1373. {
  1374.   return (compileSubExpressionWithGoto(expr, 0));
  1375. }
  1376.  
  1377.  
  1378. /*
  1379.  *    static ByteCodes compileSubExpressionWithGoto(expr, branchLen)
  1380.  *
  1381.  * Description
  1382.  *
  1383.  *    Like compileSubExpression, except that this sub expression always ends
  1384.  *    with an unconditional branch past "branchLen" bytecodes.
  1385.  *
  1386.  * Inputs
  1387.  *
  1388.  *    expr  : a TreeNode that looks like a "block".
  1389.  *    branchLen: 
  1390.  *        number of bytes to skip over, possibly zero (in which case no
  1391.  *        goto is generated).
  1392.  *
  1393.  * Outputs
  1394.  *
  1395.  *    ByteCodes that represent the execution of the statements in "expr",
  1396.  *    with a goto after them (if "branchLen" is nonzero).
  1397.  */
  1398. static ByteCodes compileSubExpressionWithGoto(expr, branchLen)
  1399. TreeNode expr;
  1400. int    branchLen;
  1401. {
  1402.   ByteCodes    currentByteCodes, subExprByteCodes;
  1403.  
  1404.   currentByteCodes = saveByteCodeArray(); /* ??? don't like this name */
  1405.  
  1406.   compileStatements(expr->vMethod.statements, false);
  1407.   if (branchLen) {
  1408.     compileJump(branchLen, unconditionalJump);
  1409.   }
  1410.  
  1411.   subExprByteCodes = getByteCodes();
  1412.   restoreByteCodeArray(currentByteCodes);
  1413.  
  1414.   return (subExprByteCodes);
  1415. }
  1416.  
  1417. /*
  1418.  *    static void compileJump(len, jumpType)
  1419.  *
  1420.  * Description
  1421.  *
  1422.  *    Compiles a jump instruction, using the smallest possible number of byte
  1423.  *    codes.  Special cases for the unconditional jump and the short false
  1424.  *    jump that the byte code interpreter handles.
  1425.  *
  1426.  * Inputs
  1427.  *
  1428.  *    len   : Number of byte codes to jump forward (only forward jumps are
  1429.  *        handled.  A C int > 0.
  1430.  *    jumpType: 
  1431.  *        An enumerated value that indicates whether the jump is
  1432.  *        unconditional, or a true or false jump.
  1433.  *
  1434.  */
  1435. static void compileJump(len, jumpType)
  1436. int    len;
  1437. JumpType jumpType;
  1438. {
  1439.   if (len <= 0) {
  1440.     errorf("Illegal length jump %d\n", len);
  1441.     longjmp(badMethod, 1);
  1442.   }
  1443.  
  1444.   switch (jumpType) {
  1445.   case unconditionalJump:
  1446.     if (len <= 8) {
  1447.       compileByte(jumpShort | (len - 1));
  1448.     } else {
  1449.       compileByte(jumpLong | (4 + len/256));
  1450.       compileByte(len & 255);
  1451.     }
  1452.     break;
  1453.  
  1454.   case falseJump:
  1455.     if (len <= 8) {
  1456.       compileByte(popJumpFalseShort | (len - 1));
  1457.     } else {
  1458.       compileByte(popJumpFalse | (len/256));
  1459.       compileByte(len & 255);
  1460.     }
  1461.     break;
  1462.  
  1463.   case trueJump:
  1464.     compileByte(popJumpTrue | (len/256));
  1465.     compileByte(len & 255);
  1466.     break;
  1467.   }
  1468. }
  1469.  
  1470. /*
  1471.  *    compilePushIntConstant(intConst)
  1472.  *
  1473.  * Description
  1474.  *
  1475.  *    Compiles an instruction to push an Integer constant on the stack.
  1476.  *    Special cases out the literals -1..2, and tries to emit the shortest
  1477.  *    possible byte sequence to get the job done.
  1478.  *
  1479.  * Inputs
  1480.  *
  1481.  *    intConst: 
  1482.  *        The constant to be pushed; a C int.
  1483.  *
  1484.  */
  1485. compilePushIntConstant(intConst)
  1486. int    intConst;
  1487. {
  1488.   TreeNode    constExpr;
  1489.   int        constIndex;
  1490.  
  1491.   if (intConst >= -1 && intConst <= 2) {
  1492.     compileByte(pushSpecial | intConst + 5);
  1493.     return;
  1494.   }
  1495.  
  1496.   /* a hack to make use of the functionality provided by addConstant */
  1497.   constExpr = makeIntConstant((long)intConst);
  1498.   constIndex = addConstant(constExpr);
  1499.   freeTree(constExpr);
  1500.   
  1501.   if (constIndex <= 31) {
  1502.     compileByte(pushLitConstant | constIndex);
  1503.   } else {
  1504.     compileByte(pushIndexed | litConstLocation);
  1505.     compileByte(constIndex);
  1506.   }
  1507. }
  1508.  
  1509.  
  1510. /*
  1511.  *    static void compileSend(selectorIndex, numArgs)
  1512.  *
  1513.  * Description
  1514.  *
  1515.  *    Compile a message send byte code.  Tries to use the minimal length byte
  1516.  *    code sequence; does not know about the special messages that the
  1517.  *    interpreter has "wired in"; those should be handled specially and this
  1518.  *    routine should not be called with them (it's ok if it is, just not
  1519.  *    quite as efficient).
  1520.  *
  1521.  * Inputs
  1522.  *
  1523.  *    selectorIndex: 
  1524.  *        The index in the literal vector of the selector for the send
  1525.  *    numArgs: 
  1526.  *        The number of arguments that the selector takes. A C integer.
  1527.  *
  1528.  */
  1529. static void compileSend(selectorIndex, numArgs)
  1530. int    selectorIndex, numArgs;
  1531. {
  1532.   if (numArgs <= 2 && selectorIndex <= 15) {
  1533.     switch (numArgs) {
  1534.     case 0:
  1535.       compileByte(sendSelectorNoArg | selectorIndex);
  1536.       break;
  1537.     case 1:
  1538.       compileByte(sendSelector1Arg | selectorIndex);
  1539.       break;
  1540.     case 2:
  1541.       compileByte(sendSelector2Arg | selectorIndex);
  1542.       break;
  1543.     }
  1544.   } else if (selectorIndex <= 31 && numArgs <= 7) {
  1545.     compileByte(sendSelector1ExtByte);
  1546.     compileByte((numArgs << 5) | selectorIndex);
  1547.   } else {
  1548.     compileByte(sendSelector2ExtByte);
  1549.     compileByte(numArgs);
  1550.     compileByte(selectorIndex);
  1551.   }
  1552. }
  1553.  
  1554. /*
  1555.  *    static void compileCascadedMessage(cascadedExpr)
  1556.  *
  1557.  * Description
  1558.  *
  1559.  *    Compiles the code for a cascaded message send.  Due to the fact that
  1560.  *    cascaded sends go to the receiver of the last message before the first
  1561.  *    cascade "operator" (the ";"), the system to perform cascaded message
  1562.  *    sends is a bit kludgy.  We basically turn on a flag to the compiler
  1563.  *    that indicates that the value of the receiver of the last message
  1564.  *    before the cascaded sends is to be duplicated; and then compile code
  1565.  *    for each cascaded expression, throwing away the result, and duplicating
  1566.  *    the original receiver so that it can be used by the current message
  1567.  *    send, and following ones.
  1568.  *
  1569.  * Inputs
  1570.  *
  1571.  *    cascadedExpr: 
  1572.  *        A tree node that represents a cascaded expression.  Both the
  1573.  *        initial receiver and all the subsequent cascaded sends can be
  1574.  *        derived from this node.
  1575.  *
  1576.  */
  1577. static void compileCascadedMessage(cascadedExpr)
  1578. TreeNode cascadedExpr;
  1579. {
  1580.   TreeNode message;
  1581.  
  1582.   dupMessageReceiver = true;
  1583.   compileExpression(cascadedExpr->vExpr.receiver);
  1584.  
  1585.   for(message = cascadedExpr->vExpr.expression; message;
  1586.       message = message->vList.next) {
  1587.     compileByte(popStackTop);
  1588.     if (message->vList.next) {
  1589.       compileByte(dupStackTop);
  1590.     }
  1591.     compileExpression(message->vList.value);
  1592.     /* !!! remember that unary, binary and keywordexpr should ignore the
  1593.        receiver field if it is nil; that is the case for these functions
  1594.        and things work out fine if that's the case. */
  1595.   }
  1596. }
  1597.  
  1598.  
  1599. /*
  1600.  *    static void compileAssignments(varList)
  1601.  *
  1602.  * Description
  1603.  *
  1604.  *    Compiles all the assignments in "varList", which is a TreeNode of type
  1605.  *    listNode.  The generated code assumes that the value on the top of the
  1606.  *    stack is what's to be used for the assignment.  Since this routine has
  1607.  *    no notion of now the value on top of the stack will be used by the
  1608.  *    calling environment, it makes sure that when the assignments are
  1609.  *    through, that the value on top of the stack after the assignment is the
  1610.  *    same as the value on top of the stack before the assignment.  The
  1611.  *    optimizer should fix this in the unnecessary cases. 
  1612.  *
  1613.  * Inputs
  1614.  *
  1615.  *    varList: 
  1616.  *            TreeNode of type listNode that contains the names of the
  1617.  *        variables to be assigned into.
  1618.  *
  1619.  */
  1620. static void compileAssignments(varList)
  1621. TreeNode varList;
  1622. {
  1623.   SymbolEntry    variable;
  1624.   int        locationIndex;
  1625.  
  1626.   for (; varList; varList = varList->vList.next) {
  1627.     variable = findVariable(varList->vList.name);
  1628.     if (variable == nil) {
  1629.       errorf("assignment to undeclared variable %s", varList->vList.name);
  1630.       longjmp(badMethod, 1);
  1631.     }
  1632.     /* Here we have several kinds of things to store: receiver variable,
  1633.        temporary variable, "literal" variable (reference by association). */
  1634.        
  1635.     if ((variable->scope == temporaryScope
  1636.     || variable->scope == receiverScope) && variable->varIndex <= 7) {
  1637.       compileByte(dupStackTop);
  1638.       compileByte(variable->scope == temporaryScope ?
  1639.           popTemporaryVariable | variable->varIndex :
  1640.           popReceiverVariable | variable->varIndex);
  1641.     } else {
  1642.       locationIndex = computeLocationIndex(variable);
  1643.       compileByte(storeIndexed);
  1644.       compileByte(locationIndex | variable->varIndex);
  1645.     }
  1646.     freeSymbolEntry(variable);
  1647.   }
  1648. }
  1649.  
  1650. /*
  1651.  *    static int computeLocationIndex(variable)
  1652.  *
  1653.  * Description
  1654.  *
  1655.  *    Given an internal representation of a variable, this routine returns
  1656.  *    an indicator of how to access the variable.  Depending on the source of
  1657.  *    the variable, this access location (which is actually to be compiled in
  1658.  *    as part of generated byte codes) can be an instance variable in the
  1659.  *    receiver, a temporary variable in the method context (includes
  1660.  *    arguments to the method) or a global or pool variable, which are
  1661.  *    referenced by a name/value Association.
  1662.  *
  1663.  * Inputs
  1664.  *
  1665.  *    variable: 
  1666.  *        Internal compiler variable.
  1667.  *
  1668.  * Outputs
  1669.  *
  1670.  *    Indication of how to access the variable.  A portion of a byte code
  1671.  *    that represents the various types of storage locations.
  1672.  */
  1673. static int computeLocationIndex(variable)
  1674. SymbolEntry variable;
  1675. {
  1676.   switch (variable->scope) {
  1677.   case receiverScope:
  1678.     return (receiverLocation);
  1679.   case temporaryScope:
  1680.     return (temporaryLocation);
  1681.   case globalScope: case poolScope:
  1682.     return (litVarLocation);
  1683.   }
  1684. }
  1685.  
  1686. /*
  1687.  *    static int isSpecialVariable(expr)
  1688.  *
  1689.  * Description
  1690.  *
  1691.  *    Examines the expression "expr" to see if it is a variable in the set
  1692.  *    "self", "false", "true", "nil".  Returns the "index" (number in 0..3)
  1693.  *    if true, -1 if false.  For this use, "super" is defined to be the
  1694.  *    equivalent of self.
  1695.  *
  1696.  * Inputs
  1697.  *
  1698.  *    expr  : TreeNode expression to be examined
  1699.  *
  1700.  * Outputs
  1701.  *
  1702.  *    -1 if expr is not a special variable
  1703.  *    0..3 if expr is "self", "true", "false", "nil", respectively.
  1704.  *    "super" is the same as self.
  1705.  */
  1706. static int isSpecialVariable(expr)
  1707. TreeNode expr;
  1708. {
  1709.   OOP        variable;
  1710.  
  1711.   if (expr->nodeType != variableNodeType) {
  1712.     return (-1);
  1713.   }
  1714.  
  1715.   variable = internString(expr->vList.name);
  1716.   if (variable == selfSymbol || variable == superSymbol) {
  1717.     return (receiverIndex);
  1718.   } else if (variable == trueSymbol) {
  1719.     return (trueIndex);
  1720.   } else if (variable == falseSymbol) {
  1721.     return (falseIndex);
  1722.   } else if (variable == nilSymbol) {
  1723.     return (nilIndex);
  1724.   } else {
  1725.     return (-1);
  1726.   }
  1727. }
  1728.  
  1729.  
  1730. /*
  1731.  *    static Boolean isSuper(expr)
  1732.  *
  1733.  * Description
  1734.  *
  1735.  *    Returns true if the expression passed to it represents the symbol
  1736.  *    "super"; false if not.
  1737.  *
  1738.  * Inputs
  1739.  *
  1740.  *    expr  : TreeNode that is an expression of some kind
  1741.  *
  1742.  * Outputs
  1743.  *
  1744.  *    true if "super" variable, false otherwise.
  1745.  */
  1746. static Boolean isSuper(expr)
  1747. TreeNode expr;
  1748. {
  1749.   if (expr->nodeType != variableNodeType) {
  1750.     return (false);
  1751.   }
  1752.  
  1753.   return (internString(expr->vList.name) == superSymbol);
  1754. }
  1755.  
  1756.  
  1757. /*
  1758.  *    static int addConstant(constExpr)
  1759.  *
  1760.  * Description
  1761.  *
  1762.  *    Scans the constants that are referenced by the current method.  If one
  1763.  *    is found that is equal to constExpr, the index of that constant is
  1764.  *    returned.  Otherwise, the constant is turned into a constant object,
  1765.  *    added to the constants of the method, and the new index is returned.
  1766.  *
  1767.  * Inputs
  1768.  *
  1769.  *    constExpr: 
  1770.  *        TreeNode of type constExprType, containing a literal constant
  1771.  *        of some kind.  Special cases -1, 0, 1, and 2 since they are
  1772.  *        available directly via instructions.
  1773.  *
  1774.  * Outputs
  1775.  *
  1776.  *    Index in the method's table of where this constant can be found,
  1777.  *    whether or not it was already there.  Returns a negative number if
  1778.  *    the constant is one of the above mentioned integers; in fact, the value
  1779.  *    returned is such that 3+returnValue is the numerical value.
  1780.  */
  1781. static int addConstant(constExpr)
  1782. TreeNode constExpr;
  1783. {
  1784.   int        i;
  1785.   long        intVal;
  1786.   OOP        constantOOP;
  1787.  
  1788.   for (i = 0; i < numLiterals; i++) {
  1789.     if (equalConstant(literalVec[i], constExpr)) {
  1790.       return (i);
  1791.     }
  1792.   }
  1793.  
  1794.   constantOOP = makeConstantOOP(constExpr);
  1795.   if (isInt(constantOOP)) {
  1796.     intVal = toInt(constantOOP);
  1797.     if (intVal >= -1 && intVal <= 2) {
  1798.       return ((int)(intVal - 3));
  1799.     }
  1800.   }
  1801.  
  1802.   return (addLiteral(constantOOP));
  1803. }
  1804.  
  1805. /*
  1806.  *    static Boolean equalConstant(oop, constExpr)
  1807.  *
  1808.  * Description
  1809.  *
  1810.  *    Returns true if "oop" and "constExpr" represent the same literal value.
  1811.  *    Primarily used by the compiler to store a single copy of duplicated
  1812.  *    literals in a method.  Can call itself in the case array literals.
  1813.  *
  1814.  * Inputs
  1815.  *
  1816.  *    oop   : An OOP that represents a constant value
  1817.  *    constExpr: 
  1818.  *        A piece of the syntax tree that represents a literal value.
  1819.  *
  1820.  * Outputs
  1821.  *
  1822.  *    True if "oop" and "constExpr" represent the same value; false
  1823.  *    otherwise. 
  1824.  */
  1825. static Boolean equalConstant(oop, constExpr)
  1826. OOP    oop;
  1827. TreeNode constExpr;
  1828. {
  1829.   TreeNode    arrayElt;
  1830.   int        len, i;
  1831.  
  1832.   /* ??? this kind of special casing of the elements of arrays bothers
  1833.      me...it should all be in one neat place. */
  1834.   if (constExpr->nodeType == symbolNodeType) { /* symbol in array constant */
  1835.     return (oop == constExpr->vExpr.selector);
  1836.   } else if (constExpr->nodeType == arrayEltListType) {
  1837.     if (isOOP(oop) && oopToObj(oop)->objClass == arrayClass) {
  1838.       for(len = 0, arrayElt = constExpr; arrayElt;
  1839.       len++, arrayElt = arrayElt->vList.next);
  1840.  
  1841.       if (len == numOOPs(oopToObj(oop))) {
  1842.     for (i = 1, arrayElt = constExpr; i <= len;
  1843.          i++, arrayElt = arrayElt->vList.next) {
  1844.       if (!equalConstant(arrayElt->vList.value, arrayAt(oop, i))) {
  1845.         return (false);
  1846.       }
  1847.     }
  1848.     return (true);
  1849.       }
  1850.     }
  1851.     return (false);
  1852.   }
  1853.  
  1854.  
  1855.  
  1856.   switch (constExpr->vConst.constType) {
  1857.   case intConst:
  1858.     if (oop == fromInt(constExpr->vConst.val.iVal)) {
  1859.       return (true);
  1860.     }
  1861.     break;
  1862.  
  1863.   case floatConst:
  1864.     if (isOOP(oop) && oopToObj(oop)->objClass == floatClass) {
  1865.       if (constExpr->vConst.val.fVal == floatOOPValue(oop)) {
  1866.     return (true);
  1867.       }
  1868.     }
  1869.     break;
  1870.  
  1871.   case charConst:
  1872.     if (oop == charOOPAt(constExpr->vConst.val.cVal)) {
  1873.       return (true);
  1874.     }
  1875.     break;
  1876.  
  1877.   case stringConst:
  1878.     if (isOOP(oop) && oopToObj(oop)->objClass == stringClass) {
  1879.       len = strlen(constExpr->vConst.val.sVal);
  1880.       if (len == stringOOPLen(oop)) {
  1881.     if (strncmp((char *)oopToObj(oop)->data, constExpr->vConst.val.sVal,
  1882.             len) == 0) {
  1883.       return (true);
  1884.     }
  1885.       }
  1886.     }
  1887.     break;
  1888.  
  1889.   case symbolConst:
  1890.     if (oop == constExpr->vConst.val.symVal) {
  1891.       return (true);
  1892.     }
  1893.     break;
  1894.     
  1895.   case arrayConst:
  1896.     if (isOOP(oop) && oopToObj(oop)->objClass == arrayClass) {
  1897.       /* ??? could keep the length in a counter */
  1898.       for(len = 0, arrayElt = constExpr->vConst.val.aVal; arrayElt;
  1899.       len++, arrayElt = arrayElt->vList.next);
  1900.       if (len == numOOPs(oopToObj(oop))) {
  1901.     for (i = 1, arrayElt = constExpr->vConst.val.aVal; i <= len;
  1902.          i++, arrayElt = arrayElt->vList.next) {
  1903.       if (!equalConstant(arrayElt->vList.value, arrayAt(oop, i))) {
  1904.         return (false);
  1905.       }
  1906.     }
  1907.     return (true);
  1908.       }
  1909.     }
  1910.     break;
  1911.   }
  1912.  
  1913.   return (false);
  1914. }
  1915.  
  1916. /*
  1917.  *    static OOP makeConstantOOP(constExpr)
  1918.  *
  1919.  * Description
  1920.  *
  1921.  *    Given a section of the syntax tree that represents a Smalltalk
  1922.  *    constant, this routine creates an OOP to be stored as a method literal
  1923.  *    in the method that's currently being compiled.
  1924.  *
  1925.  * Inputs
  1926.  *
  1927.  *    constExpr: 
  1928.  *        A portion of the tree that contains a constant value, including
  1929.  *        array constants.
  1930.  *
  1931.  * Outputs
  1932.  *
  1933.  *    An OOP that represents the constant's value.
  1934.  */
  1935. static OOP makeConstantOOP(constExpr)
  1936. TreeNode constExpr;
  1937. {
  1938.   TreeNode    arrayElt;
  1939.   int        len, i;
  1940.   OOP        resultOOP;
  1941.  
  1942.   if (constExpr->nodeType == symbolNodeType) { /* symbol in array constant */
  1943.     return (constExpr->vExpr.selector);
  1944.   } else if (constExpr->nodeType == arrayEltListType) {
  1945.     for(len = 0, arrayElt = constExpr; arrayElt;
  1946.     len++, arrayElt = arrayElt->vList.next);
  1947.     
  1948.     /* ??? this might be an uninitialized form of array creation for speed */
  1949.     resultOOP = arrayNew(len);
  1950.  
  1951.     for (i = 1, arrayElt = constExpr; i <= len;
  1952.      i++, arrayElt = arrayElt->vList.next) {
  1953.       arrayAtPut(resultOOP, i, makeConstantOOP(arrayElt->vList.value));
  1954.     }
  1955.     return (resultOOP);
  1956.   }
  1957.  
  1958.   switch (constExpr->vConst.constType) {
  1959.   case intConst:
  1960.     return (fromInt(constExpr->vConst.val.iVal));
  1961.  
  1962.   case floatConst:
  1963.     return (floatNew(constExpr->vConst.val.fVal));
  1964.  
  1965.   case charConst:
  1966.     return (charOOPAt(constExpr->vConst.val.cVal));
  1967.  
  1968.   case stringConst:
  1969.     return (stringNew(constExpr->vConst.val.sVal));
  1970.  
  1971.   case symbolConst:
  1972.     return (constExpr->vConst.val.symVal);
  1973.     
  1974.   case arrayConst:
  1975.     for(len = 0, arrayElt = constExpr->vConst.val.aVal; arrayElt;
  1976.     len++, arrayElt = arrayElt->vList.next);
  1977.     
  1978.     /* ??? this might be an uninitialized form of array creation for speed */
  1979.     resultOOP = arrayNew(len);
  1980.  
  1981.     for (i = 1, arrayElt = constExpr->vConst.val.aVal; i <= len;
  1982.      i++, arrayElt = arrayElt->vList.next) {
  1983.       arrayAtPut(resultOOP, i, makeConstantOOP(arrayElt->vList.value));
  1984.     }
  1985.     return (resultOOP);
  1986.   }
  1987.  
  1988.   return (nilOOP);
  1989. }
  1990.  
  1991. /*
  1992.  *    static int addSelector(selector)
  1993.  *
  1994.  * Description
  1995.  *
  1996.  *    Like addConstant, this routine adds "selector" to the set of selectors
  1997.  *    for the current method, and returns the index of that selector.  If the
  1998.  *    selector already existed, its index is returned.  If the selector is
  1999.  *    a special selector, then the negative of the bytecode that's associated
  2000.  *    with that special selector is returned.
  2001.  *
  2002.  * Inputs
  2003.  *
  2004.  *    selector: 
  2005.  *        A symbol that is the selector to be added to the selectors of
  2006.  *        the current method.
  2007.  *
  2008.  * Outputs
  2009.  *
  2010.  *    Index of the selector in the current method, or number < 0 which is
  2011.  *    the negative of the bytecode for a send special.
  2012.  */
  2013. static int addSelector(selector)
  2014. OOP    selector;
  2015. {
  2016.   int        builtin;
  2017.  
  2018.   if ((builtin = whichBuiltinSelector(selector)) != 0) {
  2019.     return (-builtin);
  2020.   } else {
  2021.     return (addForcedSelector(selector));
  2022.   }
  2023. }
  2024.  
  2025. /*
  2026.  *    static int whichBuiltinSelector(selector)
  2027.  *
  2028.  * Description
  2029.  *
  2030.  *    Looks for special-cased selectors, and returns a special number to
  2031.  *    indicate which selector was chosen.  If the selector isn't one of the
  2032.  *    special-cased ones, 0 is returned.
  2033.  *
  2034.  * Inputs
  2035.  *
  2036.  *    selector: 
  2037.  *        An instance of Symbol, to be special-cased.
  2038.  *
  2039.  * Outputs
  2040.  *
  2041.  *    0 if the selector isn't special-cased, otherwise an index to be used
  2042.  *    by the compiler for the selector.
  2043.  */
  2044. static int whichBuiltinSelector(selector)
  2045. OOP    selector;
  2046. {
  2047.   if (selector == atColonSymbol) {
  2048.     return (atColonSpecial);
  2049.   } else if (selector == atColonPutColonSymbol) {
  2050.     return (atColonPutColonSpecial);
  2051.   } else if (selector == sizeSymbol) {
  2052.     return (sizeSpecial);
  2053.   } else if (selector == nextSymbol) {
  2054.     return (nextSpecial);
  2055.   } else if (selector == nextPutColonSymbol) {
  2056.     return (nextPutColonSpecial);
  2057.   } else if (selector == atEndSymbol) {
  2058.     return (atEndSpecial);
  2059.   } else if (selector == classSymbol) {
  2060.     return (classSpecial);
  2061.   } else if (selector == blockCopyColonSymbol) {
  2062.     return (blockCopyColonSpecial);
  2063.   } else if (selector == valueSymbol) {
  2064.     return (valueSpecial);
  2065.   } else if (selector == valueColonSymbol) {
  2066.     return (valueColonSpecial);
  2067.   } else if (selector == doColonSymbol) {
  2068.     return (doColonSpecial);
  2069.   } else if (selector == newSymbol) {
  2070.     return (newSpecial);
  2071.   } else if (selector == newColonSymbol) {
  2072.     return (newColonSpecial);
  2073.   } else if (selector == plusSymbol) {
  2074.     return (plusSpecial);
  2075.   } else if (selector == minusSymbol) {
  2076.     return (minusSpecial);
  2077.   } else if (selector == lessThanSymbol) {
  2078.     return (lessThanSpecial);
  2079.   } else if (selector == greaterThanSymbol) {
  2080.     return (greaterThanSpecial);
  2081.   } else if (selector == lessEqualSymbol) {
  2082.     return (lessEqualSpecial);
  2083.   } else if (selector == greaterEqualSymbol) {
  2084.     return (greaterEqualSpecial);
  2085.   } else if (selector == equalSymbol) {
  2086.     return (equalSpecial);
  2087.   } else if (selector == notEqualSymbol) {
  2088.     return (notEqualSpecial);
  2089.   } else if (selector == timesSymbol) {
  2090.     return (timesSpecial);
  2091.   } else if (selector == divideSymbol) {
  2092.     return (divideSpecial);
  2093.   } else if (selector == remainderSymbol) {
  2094.     return (remainderSpecial);
  2095.   } else if (selector == bitShiftColonSymbol) {
  2096.     return (bitShiftColonSpecial);
  2097.   } else if (selector == integerDivideSymbol) {
  2098.     return (integerDivideSpecial);
  2099.   } else if (selector == bitAndColonSymbol) {
  2100.     return (bitAndColonSpecial);
  2101.   } else if (selector == bitOrColonSymbol) {
  2102.     return (bitOrColonSpecial);
  2103.   } else if (selector == sameObjectSymbol) {
  2104.     return (sameObjectSpecial);
  2105.   } else {
  2106.     return (0);
  2107.   }
  2108. }
  2109.  
  2110.  
  2111. /*
  2112.  *    static int addForcedSelector(selector)
  2113.  *
  2114.  * Description
  2115.  *
  2116.  *    Adds the given selector to the method literals, returning the index
  2117.  *    that the selector was stored under.
  2118.  *
  2119.  * Inputs
  2120.  *
  2121.  *    selector: 
  2122.  *        An instance of Symbol to be added.
  2123.  *
  2124.  * Outputs
  2125.  *
  2126.  *    Index of where in the literal vector the Symbol was stored.
  2127.  */
  2128. static int addForcedSelector(selector)     
  2129. OOP    selector;
  2130. {
  2131.  
  2132.   return (addForcedObject(selector));
  2133. }
  2134.  
  2135. /*
  2136.  *    int addForcedObject(oop)
  2137.  *
  2138.  * Description
  2139.  *
  2140.  *    Adds "oop" to the literal vector that's being created, unless it's
  2141.  *    already there.  "Already there" is defined as the exact same object is
  2142.  *    present in the literal vector.
  2143.  *
  2144.  * Inputs
  2145.  *
  2146.  *    oop   : An OOP to be added to the literal vector.
  2147.  *
  2148.  * Outputs
  2149.  *
  2150.  *    Index into the literal vector where the object was stored.  Seems like
  2151.  *    it's zero based, but I believe in practice that it's 1 based.
  2152.  */
  2153. int addForcedObject(oop)
  2154. OOP    oop;
  2155. {
  2156.   int        i;
  2157.  
  2158.   for (i = 0; i < numLiterals; i++) {
  2159.     if (literalVec[i] == oop) {
  2160.       return (i);
  2161.     }
  2162.   }
  2163.  
  2164.   return (addLiteral(oop));
  2165. }
  2166.  
  2167. /*
  2168.  *    static OOP computeSelector(selectorExpr)
  2169.  *
  2170.  * Description
  2171.  *
  2172.  *    Given a TreeNode of type keywordExprNode, this routine picks out the
  2173.  *    names selector "keywords", concatenates them, turns them into a symbol
  2174.  *    and returns that symbol.  This routine may also be called with a
  2175.  *    unaryExprType or binaryExprType tree node, in which case the selector
  2176.  *    is already known, so it is just returned.
  2177.  *
  2178.  * Inputs
  2179.  *
  2180.  *    selectorExpr:
  2181.  *        TreeNode node of type keywordExprNode, unaryExprType, or
  2182.  *        binaryExprType.
  2183.  *
  2184.  * Outputs
  2185.  *
  2186.  *    Symbol OOP that is the selector that "selectorExpr" represents.
  2187.  */
  2188. static OOP computeSelector(selectorExpr)
  2189. TreeNode selectorExpr;
  2190. {
  2191.   TreeNode    keyword;
  2192.   int        len;
  2193.   char        *nameBuf, *p;
  2194.  
  2195.   if (selectorExpr->nodeType == unaryExprType
  2196.       || selectorExpr->nodeType == binaryExprType) {
  2197.     return (selectorExpr->vExpr.selector);
  2198.   }
  2199.  
  2200.   len = 0;
  2201.   for (keyword = selectorExpr->vExpr.expression; keyword != nil;
  2202.        keyword = keyword->vList.next) {
  2203.     len += strlen(keyword->vList.name);
  2204.   }
  2205.  
  2206.   p = nameBuf = (char *)alloca(len+1);
  2207.   for (keyword = selectorExpr->vExpr.expression; keyword != nil;
  2208.        keyword = keyword->vList.next) {
  2209.     len = strlen(keyword->vList.name);
  2210.     strcpy(p, keyword->vList.name);
  2211.     p += len;
  2212.   }
  2213.  
  2214.   *p = '\0';
  2215.  
  2216.   return (internString(nameBuf));
  2217. }
  2218.  
  2219.  
  2220. /*
  2221.  *    static void addMethodClassVariable()
  2222.  *
  2223.  * Description
  2224.  *
  2225.  *    Called when a method contains at least one reference to super as a
  2226.  *    receiver, this routine makes sure that the last literal variable of the
  2227.  *    method is the class that the method is a part of.
  2228.  *
  2229.  */
  2230. static void addMethodClassVariable()
  2231. {
  2232.   addLiteral(associationNew(getClassSymbol(thisClass), thisClass));
  2233. }
  2234.  
  2235.  
  2236. /*
  2237.  *    initCompiler()
  2238.  *
  2239.  * Description
  2240.  *
  2241.  *    Prepares the compiler for execution.
  2242.  *
  2243.  */
  2244. initCompiler()
  2245. {
  2246.   hasExtendedSuper = false;
  2247.   initArgCount();
  2248.   initTempCount();
  2249.   initLiteralVec();
  2250.   initByteCodes();
  2251. }
  2252.  
  2253. /*
  2254.  *    static int listLength(listExpr)
  2255.  *
  2256.  * Description
  2257.  *
  2258.  *    Computes and returns the length of a parse tree list.
  2259.  *
  2260.  * Inputs
  2261.  *
  2262.  *    listExpr: 
  2263.  *        A list from the tree builder.
  2264.  *
  2265.  * Outputs
  2266.  *
  2267.  *    The length of the list, as an integer.
  2268.  */
  2269. static int listLength(listExpr)
  2270. TreeNode listExpr;
  2271. {
  2272.   TreeNode     l;
  2273.   long        len;
  2274.  
  2275.   for(len = 0, l = listExpr; l; l = l->vList.next, len++);
  2276.  
  2277.   if (sizeof(int) != 4) {
  2278.     if (len > (1L << (sizeof(int)*8 - 1))) {
  2279.       errorf("List too long, %ld", len);
  2280.       len = 1L << (sizeof(int)*8 - 1);
  2281.     }
  2282.   }
  2283.  
  2284.   return ((int)len);
  2285. }
  2286.  
  2287. /*
  2288.  *    static ByteCodes optimizeByteCodes(byteCodes)
  2289.  *
  2290.  * Description
  2291.  *
  2292.  *    Intended to scan the byte codes of a method, performing optimizations,
  2293.  *    and return a new vector of byte codes that represents the optimized
  2294.  *    byte code stream.  Currently a NOP.
  2295.  *
  2296.  * Inputs
  2297.  *
  2298.  *    byteCodes: 
  2299.  *        A vector of byte codes to be optimized.
  2300.  *
  2301.  * Outputs
  2302.  *
  2303.  *    Currently, the same byte codes that were passed in.
  2304.  */
  2305. static ByteCodes optimizeByteCodes(byteCodes)
  2306. ByteCodes byteCodes;
  2307. {
  2308.   /* ??? no optimization for now */
  2309.   return (byteCodes);
  2310. }
  2311.  
  2312.  
  2313. /***********************************************************************
  2314.  *
  2315.  *    Literal Vector manipulation routines.
  2316.  *
  2317.  ***********************************************************************/
  2318.  
  2319.  
  2320. /*
  2321.  *    static void initLiteralVec()
  2322.  *
  2323.  * Description
  2324.  *
  2325.  *    Prepares the literal vector for use.  The literal vector is where the
  2326.  *    compiler will store any literals that are used by the method being
  2327.  *    compiled.  The literal vector will grow as needed to accomodate the
  2328.  *    literals of the method.
  2329.  *
  2330.  */
  2331. static void initLiteralVec()
  2332. {
  2333.   numLiterals = 0;
  2334.  
  2335.   literalVecMax = LITERAL_VEC_CHUNK_SIZE;
  2336.   literalVec = (OOP *)malloc(LITERAL_VEC_CHUNK_SIZE*sizeof(OOP));
  2337. }
  2338.  
  2339. /*
  2340.  *    static int addLiteral(OOP)
  2341.  *
  2342.  * Description
  2343.  *
  2344.  *    Adds "OOP" to the literals associated with the method being compiled
  2345.  *    and returns the index of the literal slot that was used (1 based).
  2346.  *
  2347.  * Inputs
  2348.  *
  2349.  *    oop:    OOP to add to the literal vector.
  2350.  *
  2351.  * Outputs
  2352.  *
  2353.  *    Index (1 based) in the literal vector of where the OOP was added.
  2354.  */
  2355. static int addLiteral(oop)
  2356. OOP    oop;
  2357. {
  2358.   if (numLiterals >= literalVecMax) {
  2359.     reallocLiteralVec();
  2360.   }
  2361.  
  2362.   literalVec[numLiterals] = oop;
  2363.   return (numLiterals++);
  2364. }
  2365.  
  2366. /*
  2367.  *    static void reallocLiteralVec()
  2368.  *
  2369.  * Description
  2370.  *
  2371.  *    Called to grow the literal vector that the compiler is using.  Modifies
  2372.  *    the global variables "literalVec" and "literalVecMax" to reflect the
  2373.  *    growth. 
  2374.  *
  2375.  */
  2376. static void reallocLiteralVec()
  2377. {
  2378.   literalVecMax += LITERAL_VEC_CHUNK_SIZE;
  2379.   literalVec = (OOP *)realloc(literalVec, literalVecMax * sizeof(OOP));
  2380. }
  2381.  
  2382.  
  2383. /*
  2384.  *    OOP getMethodLiterals()
  2385.  *
  2386.  * Description
  2387.  *
  2388.  *    Creates a new array object that contains the literals for the method
  2389.  *    that's being compiled and returns it.  As a side effect, the currently
  2390.  *    allocated working literal vector is freed.  If there were no literals
  2391.  *    for the current method, nilOOP is returned.
  2392.  *
  2393.  * Outputs
  2394.  *
  2395.  *    The newly created array object, or nilOOP.
  2396.  */
  2397. static OOP getMethodLiterals()
  2398. {
  2399.   OOP        methodLiterals;
  2400.   int        i;
  2401.  
  2402.   if (numLiterals == 0) {
  2403.     return (nilOOP);
  2404.   }
  2405.  
  2406.   if (numLiterals > MAX_NUM_LITERALS) {
  2407.     errorf("Maximum number of literals, %d, exceeded: %d.  Extras ignored",
  2408.        MAX_NUM_LITERALS, numLiterals);
  2409.     numLiterals = MAX_NUM_LITERALS;
  2410.     hadError = true;
  2411.   }
  2412.  
  2413.   methodLiterals = arrayNew(numLiterals);
  2414.   for (i = 0; i < numLiterals; i++) {
  2415.     arrayAtPut(methodLiterals, i+1, literalVec[i]);
  2416.   }
  2417.  
  2418.   free(literalVec);
  2419.  
  2420.   return (methodLiterals);
  2421. }
  2422.  
  2423. /*
  2424.  *    static void installMethod(selector, primitiveIndex, numArgs, numTemps, literals, byteCodes)
  2425.  *
  2426.  * Description
  2427.  *
  2428.  *    Creates a new CompileMethod and installs it in the method dictionary
  2429.  *    for the current class.  If the current class does not contain a valid
  2430.  *    method dictionary, one is allocated for it.
  2431.  *
  2432.  * Inputs
  2433.  *
  2434.  *    selector: 
  2435.  *        A symbol that the compiled method should be stored under.  This
  2436.  *        selector is what will be matched against the message selector
  2437.  *        when a message is sent to the class that this method is a part
  2438.  *        of.
  2439.  *    primitiveIndex: 
  2440.  *        A C integer for the primitive operation associated with this
  2441.  *        method.  0 if no primitive is associated with this method.
  2442.  *    numArgs: 
  2443.  *        A C integer for the number of arguments that this method has.
  2444.  *    numTemps: 
  2445.  *        A C integer for the number of temporaries that this method has.
  2446.  *    literals: 
  2447.  *        An Array of method literals.
  2448.  *    byteCodes: 
  2449.  *        Vector of the bytecodes for the CompiledMethod.
  2450.  *
  2451.  */
  2452. static void installMethod(selector, primitiveIndex, numArgs, numTemps,
  2453.               literals, byteCodes)
  2454. OOP    selector, literals;
  2455. int    primitiveIndex, numArgs, numTemps;
  2456. ByteCodes byteCodes;
  2457. {
  2458.   OOP        method, methodDictionaryOOP;
  2459.  
  2460.   if (declareTracing) {
  2461.     printf("Class "); printObject(thisClass); printf("\n");
  2462.     printf("   "); printSymbol(selector); printf("\n");
  2463.   }
  2464.  
  2465.   methodDictionaryOOP = validClassMethodDictionary(thisClass);
  2466.   method = makeNewMethod(primitiveIndex, numArgs, numTemps, literals,
  2467.              byteCodes);
  2468.  
  2469.   identityDictionaryAtPut(methodDictionaryOOP, selector, method); 
  2470.   updateMethodCache(selector, thisClass, method);
  2471. }
  2472.  
  2473. /*
  2474.  *    static OOP makeNewMethod(primitiveIndex, numArgs, numTemps, literals, byteCodes)
  2475.  *
  2476.  * Description
  2477.  *
  2478.  *    Constructs and returns a new CompiledMethod instance.  It computes the
  2479.  *    method header based on its arguments, and on the contents of the
  2480.  *    method's byte codes.
  2481.  *
  2482.  * Inputs
  2483.  *
  2484.  *    primitiveIndex: 
  2485.  *        Integer, non-zero indicates that the method has a primitive
  2486.  *        method associated with it.  The primitive will be invoked first
  2487.  *        when the method is invoked, and only if the primitive signals
  2488.  *        failure will the remaining byte codes of the method be
  2489.  *        executed.
  2490.  *    numArgs: 
  2491.  *        Number of arguments that the method has.  A C integer.
  2492.  *    numTemps: 
  2493.  *        Number of temporaries that the method has. C integer.
  2494.  *    literals: 
  2495.  *        An Array of method literals that the CompiledMethod should use.
  2496.  *    byteCodes: 
  2497.  *        A vector of byte codes that make up the executable part of the
  2498.  *        method. 
  2499.  *
  2500.  * Outputs
  2501.  *
  2502.  *    A newly allocated CompiledMethod, fully initialized and ready to go.
  2503.  */
  2504. static OOP makeNewMethod(primitiveIndex, numArgs, numTemps, literals,
  2505.              byteCodes)
  2506. int    primitiveIndex, numArgs, numTemps;
  2507. OOP    literals;
  2508. ByteCodes byteCodes;
  2509. {
  2510.   MethodHeader    header;
  2511.   int        newFlags;
  2512.  
  2513.   header.intMark = 1;
  2514.   header.headerFlag = 0;
  2515.   if (primitiveIndex) {
  2516.     header.headerFlag = 3;
  2517.     if (declareTracing) {
  2518.       printf("  Primitive Index %d\n", primitiveIndex);
  2519.     }
  2520.   }
  2521.  
  2522.   header.primitiveIndex = primitiveIndex;
  2523.   header.numArgs = numArgs;
  2524.   header.numTemps = numTemps;
  2525.   header.numLiterals = numLiterals;
  2526.  
  2527.   if (primitiveIndex == 0) {
  2528.     if (numArgs == 0 && numTemps == 0
  2529.     && (newFlags = isSimpleReturn(byteCodes)) != 0) {
  2530.       header.headerFlag = newFlags & 0xFF;
  2531.       if (header.headerFlag == 2) {
  2532.     /* if returning an instance variable, this is indicated in the number
  2533.        of temporary variables */
  2534.     header.numTemps = newFlags >> 8;
  2535.       }
  2536.       freeByteCodes(byteCodes);
  2537.       byteCodes = nil;
  2538.       literals = nilOOP;
  2539.     }
  2540.   }
  2541.   return (methodNew(header, literals, byteCodes));
  2542. }
  2543.  
  2544. /*
  2545.  *    static OOP methodNew(header, literals, byteCodes)
  2546.  *
  2547.  * Description
  2548.  *
  2549.  *    Creates and returns a CompiledMethod.  The method is completely filled
  2550.  *    in, including the descriptor, the method literals, and the byte codes
  2551.  *    for the method.
  2552.  *
  2553.  * Inputs
  2554.  *
  2555.  *    header: Header of the method, a Smalltalk Integer.
  2556.  *    literals: 
  2557.  *        A Smalltalk Array of literals that the method should contain.
  2558.  *    byteCodes: 
  2559.  *        The byte code vector for the method.
  2560.  *
  2561.  * Outputs
  2562.  *
  2563.  *    A newly created CompiledMethod instance that's completely initialized.
  2564.  */
  2565. static OOP methodNew(header, literals, byteCodes)
  2566. MethodHeader header;
  2567. OOP    literals;
  2568. ByteCodes byteCodes;
  2569. {
  2570.   int        numByteCodes, numLiterals, i;
  2571.   CompiledMethod method;
  2572.   OOP        *oopPtr, litOOP, methodOOP;
  2573.  
  2574.   if (byteCodes != nil) {
  2575.     numByteCodes = byteCodeLength(byteCodes);
  2576.   } else {
  2577.     numByteCodes = 0;
  2578.   }
  2579.  
  2580.   method = simpleMethodNew(numByteCodes, header);
  2581.   method->descriptor = methodInfoNew();
  2582.   maybeMoveOOP(method->descriptor);
  2583.  
  2584.   numLiterals = header.numLiterals;
  2585.  
  2586.   for (i = 1; i <= numLiterals; i++) {
  2587.     litOOP = arrayAt(literals, i);
  2588.     maybeMoveOOP(litOOP);
  2589.     method->literals[i-1] = litOOP;
  2590.   }
  2591.  
  2592.   if (byteCodes != nil) {
  2593.     copyByteCodes((Byte *)&method->literals[numLiterals], byteCodes);
  2594.   }
  2595.  
  2596.   if (declareTracing) {
  2597.     printByteCodes(byteCodes, method->literals);
  2598.   }
  2599.  
  2600.   freeByteCodes(byteCodes);
  2601.  
  2602.   methodOOP = allocOOP(method);
  2603.   methodOOP->emptyBytes = (4 - numByteCodes) & 3;
  2604.   return (methodOOP);
  2605. }
  2606.  
  2607. /*
  2608.  *    OOP methodNewOOP(numByteCodes, header)
  2609.  *
  2610.  * Description
  2611.  *
  2612.  *    Used to implement the primitive that creates new compiled methods.
  2613.  *    Allocates and returns an OOP for a CompiledMethod.
  2614.  *
  2615.  * Inputs
  2616.  *
  2617.  *    numByteCodes: 
  2618.  *        Number of byte codes that the CompiledMethod will contain.
  2619.  *    header: The header for the CompiledMethod.  A Smalltalk integer.
  2620.  *
  2621.  * Outputs
  2622.  *
  2623.  *    Newly allocated CompiledMethod OOP.
  2624.  */
  2625. OOP methodNewOOP(numByteCodes, header)
  2626. long    numByteCodes;
  2627. MethodHeader header;
  2628. {
  2629.   CompiledMethod method;
  2630.   OOP        oop;
  2631.  
  2632.   method = simpleMethodNew(numByteCodes, header);
  2633.   oop = allocOOP(method);
  2634.   oop->emptyBytes = (4 - numByteCodes) & 3;
  2635.  
  2636.   return (oop);
  2637. }
  2638.  
  2639. /*
  2640.  *    static CompiledMethod simpleMethodNew(numByteCodes, header)
  2641.  *
  2642.  * Description
  2643.  *
  2644.  *    Creates and returns a compiled method object.  It sets the header of
  2645.  *    the compiled method from "header", and allocates the object so that it
  2646.  *    can hold "numByteCodes" byte codes.
  2647.  *
  2648.  * Inputs
  2649.  *
  2650.  *    numByteCodes: 
  2651.  *        An integer that is the number of byte codes the compiled method
  2652.  *        will contain.
  2653.  *    header: The method header to be used.  Used for placing into the
  2654.  *        compiled method and for figuring out how many literals the
  2655.  *        method has.
  2656.  *
  2657.  * Outputs
  2658.  *
  2659.  *    A compiled method object (NOT an OOP!).
  2660.  */
  2661. static CompiledMethod simpleMethodNew(numByteCodes, header)
  2662. long    numByteCodes;
  2663. MethodHeader header;
  2664. {
  2665.   CompiledMethod method;
  2666.   int        numBytes, numLiterals, i;
  2667.  
  2668.   numBytes = sizeof(struct CompiledMethodStruct) - sizeof(method->literals);
  2669.   numLiterals = 0;
  2670.  
  2671.   numLiterals = header.numLiterals;
  2672.   numBytes += numLiterals * sizeof(OOP);
  2673.  
  2674.   numBytes += numByteCodes;
  2675.  
  2676.   method = (CompiledMethod)allocObj(ROUNDED_WORDS(numBytes) << 2);
  2677.   method->objSize = ROUNDED_WORDS(numBytes);
  2678.   method->objClass = compiledMethodClass;
  2679.   method->header = header;
  2680.  
  2681.   return (method);
  2682. }
  2683.  
  2684. /*
  2685.  *    Boolean validMethodIndex(methodOOP, index)
  2686.  *
  2687.  * Description
  2688.  *
  2689.  *    Returns true if "index" is in the range of valid indices into the
  2690.  *    instance variables of CompiledMethod "methodOOP".
  2691.  *
  2692.  * Inputs
  2693.  *
  2694.  *    methodOOP: 
  2695.  *        An instance of CompiledMethod.
  2696.  *    index : 1 based integer index into the instance variables of the
  2697.  *        method.
  2698.  *
  2699.  * Outputs
  2700.  *
  2701.  *    True if "index" is within legal range of indices to the instance
  2702.  *    variables of the method.  False otherwise.
  2703.  */
  2704. Boolean validMethodIndex(methodOOP, index)
  2705. OOP    methodOOP;
  2706. long    index;
  2707. {
  2708.   CompiledMethod method;
  2709.  
  2710.   method = (CompiledMethod)oopToObj(methodOOP);
  2711.   /* Written this way to allow a debugging person to see the return value */
  2712.   /* The +2 counts for the description and header */
  2713.   if (index >= 1 && index <= method->header.numLiterals + 2) {
  2714.     return (true);
  2715.   } else {
  2716.     return (false);
  2717.   }
  2718. }
  2719.  
  2720. /*
  2721.  *    OOP compiledMethodAt(methodOOP, index)
  2722.  *
  2723.  * Description
  2724.  *
  2725.  *    Return the object at "index" in CompiledMethod "methodOOP".  Method
  2726.  *    literals currently start at index 3.
  2727.  *
  2728.  * Inputs
  2729.  *
  2730.  *    methodOOP: 
  2731.  *        An instance of CompiledMethod.
  2732.  *    index : A 1 based integer index into the instance variables of the
  2733.  *        method.  The method's descriptor is at index 1, and the method
  2734.  *        header is at index 2.
  2735.  *
  2736.  * Outputs
  2737.  *
  2738.  *    The object at the given index.
  2739.  */
  2740. OOP compiledMethodAt(methodOOP, index)
  2741. OOP    methodOOP;
  2742. long    index;
  2743. {
  2744.   CompiledMethod method;
  2745.   OOP        oop;
  2746.  
  2747.   method = (CompiledMethod)oopToObj(methodOOP);
  2748.   oop = method->literals[index-3]; /* index==1 => descriptor
  2749.                       => literals[index-2-1] */
  2750.   return (oop);
  2751. }
  2752.  
  2753. /*
  2754.  *    void compiledMethodAtPut(methodOOP, index, valueOOP)
  2755.  *
  2756.  * Description
  2757.  *
  2758.  *    Store "valueOOP" into the instance variable of CompiledMethod
  2759.  *    "methodOOP" at "index".
  2760.  *
  2761.  * Inputs
  2762.  *
  2763.  *    methodOOP: 
  2764.  *        A CompiledMethod instance.
  2765.  *    index : A 1 based index into the method's instance variables.  Method
  2766.  *        literals currently start at index 3; the descriptor is stored
  2767.  *        at index 1, and the header is at index 2.
  2768.  *    valueOOP: 
  2769.  *        The object to be stored at the given index.
  2770.  *
  2771.  */
  2772. void compiledMethodAtPut(methodOOP, index, valueOOP)
  2773. OOP    methodOOP, valueOOP;
  2774. long    index;
  2775. {
  2776.   CompiledMethod method;
  2777.   OOP        oop;
  2778.  
  2779.   method = (CompiledMethod)oopToObj(methodOOP);
  2780.   prepareToStore(methodOOP, valueOOP);
  2781.   method->literals[index-3] = valueOOP; /*index==1 => descriptor
  2782.                       => literals[index-2-1] */
  2783. }
  2784.  
  2785. /*
  2786.  *    OOP getMethodDescriptor(methodOOP)
  2787.  *
  2788.  * Description
  2789.  *
  2790.  *    Returns the descriptor for the given CompiledMethod.  The descriptor
  2791.  *    contains information about which category the method was stored under,
  2792.  *    and information that can be used to reconstruct the source code for the
  2793.  *    method.
  2794.  *
  2795.  * Inputs
  2796.  *
  2797.  *    methodOOP: 
  2798.  *        OOP of a CompiledMethod instance.
  2799.  *
  2800.  * Outputs
  2801.  *
  2802.  *    Descriptor that was stored in the method, normally a MethodInfo
  2803.  *    instance.
  2804.  */
  2805. OOP getMethodDescriptor(methodOOP)
  2806. OOP    methodOOP;
  2807. {
  2808.   CompiledMethod method;
  2809.  
  2810.   method = (CompiledMethod)oopToObj(methodOOP);
  2811.   return (method->descriptor);
  2812. }
  2813.  
  2814. /*
  2815.  *    void setMethodDescriptor(methodOOP, descriptorOOP)
  2816.  *
  2817.  * Description
  2818.  *
  2819.  *    Sets the method descriptor of "methodOOP" to be "descriptorOOP".
  2820.  *
  2821.  * Inputs
  2822.  *
  2823.  *    methodOOP: 
  2824.  *        OOP of a CompiledMethod.
  2825.  *    descriptorOOP: 
  2826.  *        OOP of a MethodInfo instance which contains the descriptor for
  2827.  *        the CompiledMethod. 
  2828.  *
  2829.  */
  2830. void setMethodDescriptor(methodOOP, descriptorOOP)
  2831. OOP    methodOOP, descriptorOOP;
  2832. {
  2833.   CompiledMethod method;
  2834.  
  2835.   method = (CompiledMethod)oopToObj(methodOOP);
  2836.   if (GCIsOn()) {
  2837.     prepareToStore(methodOOP, descriptorOOP);
  2838.   }
  2839.   method->descriptor = descriptorOOP;
  2840. }
  2841.  
  2842. /*
  2843.  *    static OOP methodInfoNew()
  2844.  *
  2845.  * Description
  2846.  *
  2847.  *    Returns an instance of MethodInfo.  This instance is used in the
  2848.  *    reconstruction of the source code for the method, and holds the
  2849.  *    category that the method belongs to.
  2850.  *
  2851.  * Outputs
  2852.  *
  2853.  *    Instance of MethodInfo used to hold category and source string
  2854.  *    information.
  2855.  */
  2856. static OOP methodInfoNew()
  2857. {
  2858.   MethodInfo    methodInfo;
  2859.  
  2860.   methodInfo = (MethodInfo)newInstance(methodInfoClass);
  2861.   methodInfo->sourceCode = fileSegmentNew();
  2862.   maybeMoveOOP(methodInfo->sourceCode);
  2863.   methodInfo->category = thisCategory;
  2864.  
  2865.   return (allocOOP(methodInfo));
  2866. }
  2867.  
  2868. /*
  2869.  *    static OOP fileSegmentNew()
  2870.  *
  2871.  * Description
  2872.  *
  2873.  *    Returns a FileSegment instance for the currently open compilation
  2874.  *    stream.   FileSegment instances are used record information useful in
  2875.  *    obtaining the source code for a method that's been compiled.  Depending
  2876.  *    on whether the input stream is a string or a FileStream, the instance
  2877.  *    variables are different; for a string, the entire contents of the
  2878.  *    string is preserved as the source code for the method; for a disk file,
  2879.  *    the file name, byte offset and length are preserved.
  2880.  *
  2881.  * Outputs
  2882.  *
  2883.  *    A FileSegment instance that can be used to recover the current method's
  2884.  *    source code.
  2885.  */
  2886. static OOP fileSegmentNew()
  2887. {
  2888.   OOP        fileName, stringContents;
  2889.   FileSegment    fileSegment;
  2890.   int        startPos;
  2891.  
  2892.   switch (getCurStreamType()) {
  2893.   case unknownStreamType:
  2894.     return (nilOOP);
  2895.  
  2896.   case fileStreamType: 
  2897.     fileName = getCurFileName();
  2898.     fileSegment = (FileSegment)newInstance(fileSegmentClass);
  2899.     maybeMoveOOP(fileName);
  2900.     fileSegment->fileName = fileName;
  2901.     startPos = getMethodStartPos();
  2902.     fileSegment->startPos = fromInt(startPos);
  2903.     /* The second -1 removes the terminating '!' */
  2904.     fileSegment->length = fromInt(getCurFilePos() - startPos - 1 - 1);
  2905.     return (allocOOP(fileSegment));
  2906.  
  2907.   case stringStreamType:
  2908.     stringContents = getCurString();
  2909.     return (stringContents);
  2910.  
  2911. #ifdef USE_READLINE
  2912.   case readlineStreamType:
  2913.     stringContents = getCurReadline();
  2914.     return (stringContents);
  2915. #endif /* USE_READLINE */
  2916.   }
  2917. }
  2918.